класс BigDecimal
BigDecimal предоставляет арифметику с плавающей точкой и произвольной точностью для десятичных чисел.
Введение
Ruby предоставляет встроенную поддержку арифметики с целыми числами произвольной точности.
Например:
42**13 #=> 1265437718438866624512
BigDecimal предоставляет аналогичную поддержку для очень больших или очень точных чисел с плавающей точкой.
Десятичная арифметика также полезна для общих вычислений, потому что она дает правильные ответы, ожидаемые пользователями, в то время как обычная двоичная арифметика с плавающей точкой часто вносит незначительные ошибки из-за преобразования между системами счисления с основанием 10 и 2.
Например, попробуйте:
sum = 0 10_000.times do sum = sum + 0.0001 end print sum #=> 0.9999999999999062
и сравните с результатом:
require 'bigdecimal'
sum = BigDecimal("0")
10_000.times do
sum = sum + BigDecimal("0.0001")
end
print sum #=> 0.1E1
Аналогично:
(BigDecimal("1.2") - BigDecimal("1.0")) == BigDecimal("0.2") #=> true
(1.2 - 1.0) == 0.2 #=> false
Примечание о точности
Для вычисления с использованием BigDecimal и другого value, точность результата зависит от типа value:
-
Если
valueявляется Float, точность составляет Float::DIG + 1. -
Если
valueявляется Rational, точность больше, чем Float::DIG + 1. -
Если
valueявляется BigDecimal, точность составляет точностьvalueво внутренней форме представления, которая зависит от платформы. -
Если
valueэто другой объект, точность определяется результатом +BigDecimal(value)+.
Особенности точной десятичной арифметики
Поскольку BigDecimal более точна, чем обычная двоичная арифметика с плавающей точкой, она требует некоторых специальных значений.
Бесконечность
BigDecimal иногда необходимо возвращать бесконечность, например, если вы делите значение на ноль.
BigDecimal("1.0") / BigDecimal("0.0") #=> Infinity
BigDecimal("-1.0") / BigDecimal("0.0") #=> -Infinity
Вы можете представить бесконечные числа для BigDecimal с помощью строк 'Infinity', '+Infinity' и '-Infinity' (регистр важен).
Не число (NaN)
Когда вычисление приводит к неопределённому значению, возвращается специальное значение NaN (для «не числа»).
Пример:
BigDecimal("0.0") / BigDecimal("0.0") #=> NaN
Вы также можете создать неопределённые значения.
NaN никогда не считается равным никакому другому значению, даже самому себе:
n = BigDecimal('NaN')
n == 0.0 #=> false
n == n #=> false
Положительный и отрицательный ноль
Если вычисление приводит к значению, которое слишком мало, чтобы быть представленным как BigDecimal в текущих заданных пределах точности, возвращается ноль.
Если значение, которое слишком мало для представления, отрицательно, возвращается значение отрицательного нуля BigDecimal.
BigDecimal("1.0") / BigDecimal("-Infinity") #=> -0.0
Если значение положительно, возвращается значение положительного нуля.
BigDecimal("1.0") / BigDecimal("Infinity") #=> 0.0
(См. BigDecimal.mode о том, как задать пределы точности.)
Обратите внимание, что -0.0 и 0.0 считаются одинаковыми для сравнения.
Также обратите внимание, что в математике нет особого понятия отрицательного или положительного нуля; истинное математическое нуль не имеет знака.
bigdecimal/util
Когда вам требуется bigdecimal/util, метод to_d будет доступен для BigDecimal и для встроенных классов Integer, Float, Rational и String:
require 'bigdecimal/util' 42.to_d # => 0.42e2 0.5.to_d # => 0.5e0 (2/3r).to_d(3) # => 0.667e0 "0.5".to_d # => 0.5e0
Методы для работы с JSON
-
::json_create: Возвращает новый объект BigDecimal, созданный из данного объекта.
-
#as_json: Возвращает хеш из 2 элементов, представляющий
self. -
#to_json: Возвращает строку JSON, представляющую
self.
Эти методы предоставляются драгоценностью JSON. Чтобы сделать эти методы доступными:
require 'json/add/bigdecimal'
-
Лицензия
Авторское право © 2002 Шигео Кобаяси <shigeo@tinyforest.gr.jp>.
BigDecimal распространяется под лицензиями Ruby и 2-х пунктов BSD. Подробности см. в файле LICENSE.txt.
Поддерживается mrkn <mrkn@mrkn.jp> и членами ruby-core.
Документировано zzak <zachary@zacharyscott.net>, mathew <meta@pobox.com> и многими другими участниками.
Константы
- BASE
-
Значение основания, используемое во внутренних вычислениях. На 32-битной системе
BASEравно 10000, что указывает на то, что вычисления выполняются группами по 4 цифры. (Если бы оно было больше, BASE**2 не поместилось бы в 32 бита, поэтому вы не могли гарантировать, что две группы всегда могли бы быть перемножены без переполнения.) - EXCEPTION_ALL
-
Определяет, будут ли переполнение, недополнение или деление на ноль приводить к выбрасыванию исключения. См.
BigDecimal.mode. - EXCEPTION_INFINITY
-
Определяет, что произойдёт, когда результат вычисления равен бесконечности. См.
BigDecimal.mode. - EXCEPTION_NaN
-
Определяет, что произойдёт, когда результат вычисления — не число (NaN). См.
BigDecimal.mode. - EXCEPTION_OVERFLOW
-
Определяет, что произойдёт, когда результат вычисления — переполнение (результат слишком большой для представления). См.
BigDecimal.mode. - EXCEPTION_UNDERFLOW
-
Определяет, что произойдёт, когда результат вычисления — недополнение (результат слишком мал для представления). См.
BigDecimal.mode. - EXCEPTION_ZERODIVIDE
-
Определяет, что произойдёт при делении на ноль. См.
BigDecimal.mode. - INFINITY
-
Специальные константы значений
- NAN
- ROUND_CEILING
-
Округление вверх до +Бесконечности. См.
BigDecimal.mode. - ROUND_DOWN
-
Указывает, что значения должны быть округлены к нулю. См.
BigDecimal.mode. - ROUND_FLOOR
-
Округление вниз до -Бесконечности. См.
BigDecimal.mode. - ROUND_HALF_DOWN
-
Указывает, что цифры >= 6 должны быть округлены вверх, а остальные — вниз. См.
BigDecimal.mode. - ROUND_HALF_EVEN
-
Округление к ближайшему чётному соседу. См.
BigDecimal.mode. - ROUND_HALF_UP
-
Указывает, что цифры >= 5 должны быть округлены вверх, а остальные — вниз. См.
BigDecimal.mode. - ROUND_MODE
-
Определяет, что произойдёт, когда результат необходимо округлить, чтобы он поместился в нужное количество значащих цифр. См.
BigDecimal.mode. - ROUND_UP
-
Указывает, что значения должны быть округлены от нуля. См.
BigDecimal.mode. - SIGN_NEGATIVE_FINITE
-
Указывает, что значение отрицательное и конечное. См.
BigDecimal.sign. - SIGN_NEGATIVE_INFINITE
-
Указывает, что значение отрицательное и бесконечное. См.
BigDecimal.sign. - SIGN_NEGATIVE_ZERO
-
Указывает, что значение равно -0. См.
BigDecimal.sign. - SIGN_NaN
-
Указывает, что значение — не число. См.
BigDecimal.sign. - SIGN_POSITIVE_FINITE
-
Указывает, что значение положительное и конечное. См.
BigDecimal.sign. - SIGN_POSITIVE_INFINITE
-
Указывает, что значение положительное и бесконечное. См.
BigDecimal.sign. - SIGN_POSITIVE_ZERO
-
Указывает, что значение равно +0. См.
BigDecimal.sign. - VERSION
-
Версия библиотеки bigdecimal
Методы публичного класса
static VALUE
BigDecimal_load(VALUE self, VALUE str)
{
ENTER(2);
Real *pv;
unsigned char *pch;
unsigned char ch;
unsigned long m=0;
pch = (unsigned char *)StringValueCStr(str);
/* First get max prec */
while((*pch) != (unsigned char)'\0' && (ch = *pch++) != (unsigned char)':') {
if(!ISDIGIT(ch)) {
rb_raise(rb_eTypeError, "load failed: invalid character in the marshaled string");
}
m = m*10 + (unsigned long)(ch-'0');
}
if (m > VpBaseFig()) m -= VpBaseFig();
GUARD_OBJ(pv, VpNewRbClass(m, (char *)pch, self, true, true));
m /= VpBaseFig();
if (m && pv->MaxPrec > m) {
pv->MaxPrec = m+1;
}
return VpCheckGetValue(pv);
} Внутренний метод, используемый для поддержки маршалинга. Смотрите модуль Marshal.
static inline VALUE
BigDecimal_double_fig(VALUE self)
{
return INT2FIX(VpDblFig());
} Возвращает количество цифр, которое может иметь объект Float; результат зависит от системы:
BigDecimal.double_fig # => 16
static VALUE
BigDecimal_s_interpret_loosely(VALUE klass, VALUE str)
{
char const *c_str = StringValueCStr(str);
Real *vp = VpNewRbClass(0, c_str, klass, false, true);
if (!vp)
return Qnil;
else
return VpCheckGetValue(vp);
} # File ext/json/lib/json/add/bigdecimal.rb, line 13 def self.json_create(object) BigDecimal._load object['b'] end
См. as_json.
static VALUE
BigDecimal_limit(int argc, VALUE *argv, VALUE self)
{
VALUE nFig;
VALUE nCur = SIZET2NUM(VpGetPrecLimit());
if (rb_scan_args(argc, argv, "01", &nFig) == 1) {
int nf;
if (NIL_P(nFig)) return nCur;
nf = NUM2INT(nFig);
if (nf < 0) {
rb_raise(rb_eArgError, "argument must be positive");
}
VpSetPrecLimit(nf);
}
return nCur;
} Ограничить количество значащих цифр в вновь созданных числах BigDecimal указанным значением. Округление выполняется при необходимости, согласно BigDecimal.mode.
Предел 0, по умолчанию, означает отсутствие верхнего предела.
Установленный этим методом предел имеет меньший приоритет, чем любой предел, указанный для методов экземпляров, таких как ceil, floor, truncate или round.
static VALUE
BigDecimal_mode(int argc, VALUE *argv, VALUE self)
{
VALUE which;
VALUE val;
unsigned long f,fo;
rb_scan_args(argc, argv, "11", &which, &val);
f = (unsigned long)NUM2INT(which);
if (f & VP_EXCEPTION_ALL) {
/* Exception mode setting */
fo = VpGetException();
if (val == Qnil) return INT2FIX(fo);
if (val != Qfalse && val!=Qtrue) {
rb_raise(rb_eArgError, "second argument must be true or false");
return Qnil; /* Not reached */
}
if (f & VP_EXCEPTION_INFINITY) {
VpSetException((unsigned short)((val == Qtrue) ? (fo | VP_EXCEPTION_INFINITY) :
(fo & (~VP_EXCEPTION_INFINITY))));
}
fo = VpGetException();
if (f & VP_EXCEPTION_NaN) {
VpSetException((unsigned short)((val == Qtrue) ? (fo | VP_EXCEPTION_NaN) :
(fo & (~VP_EXCEPTION_NaN))));
}
fo = VpGetException();
if (f & VP_EXCEPTION_UNDERFLOW) {
VpSetException((unsigned short)((val == Qtrue) ? (fo | VP_EXCEPTION_UNDERFLOW) :
(fo & (~VP_EXCEPTION_UNDERFLOW))));
}
fo = VpGetException();
if(f & VP_EXCEPTION_ZERODIVIDE) {
VpSetException((unsigned short)((val == Qtrue) ? (fo | VP_EXCEPTION_ZERODIVIDE) :
(fo & (~VP_EXCEPTION_ZERODIVIDE))));
}
fo = VpGetException();
return INT2FIX(fo);
}
if (VP_ROUND_MODE == f) {
/* Rounding mode setting */
unsigned short sw;
fo = VpGetRoundMode();
if (NIL_P(val)) return INT2FIX(fo);
sw = check_rounding_mode(val);
fo = VpSetRoundMode(sw);
return INT2FIX(fo);
}
rb_raise(rb_eTypeError, "first argument for BigDecimal.mode invalid");
return Qnil;
} Возвращает целое число, представляющее параметры режима для обработки исключений и округления.
Эти режимы управляют обработкой исключений:
-
BigDecimal::EXCEPTION_NaN.
-
BigDecimal::EXCEPTION_INFINITY.
-
BigDecimal::EXCEPTION_UNDERFLOW.
-
BigDecimal::EXCEPTION_OVERFLOW.
-
BigDecimal::EXCEPTION_ZERODIVIDE.
-
BigDecimal::EXCEPTION_ALL.
Значения для setting для обработки исключений:
-
true: устанавливает указанныйmodeвtrue. -
false: устанавливает указанныйmodeвfalse. -
nil: не изменяет настройки режима.
Вы можете использовать метод BigDecimal.save_exception_mode для временного изменения и последующего автоматического восстановления режимов обработки исключений.
Для ясности, некоторые примеры ниже начинают с установки всех режимов обработки исключений в false.
Этот режим управляет способом выполнения округления:
-
BigDecimal::ROUND_MODE
Вы можете использовать метод BigDecimal.save_rounding_mode для временного изменения и последующего автоматического восстановления режима округления.
NaN
Режим BigDecimal::EXCEPTION_NaN управляет поведением при создании BigDecimal NaN.
Настройки:
-
false(по умолчанию): ВозвращаетBigDecimal('NaN'). -
true: ВызываетFloatDomainError.
Примеры:
BigDecimal.mode(BigDecimal::EXCEPTION_ALL, false) # => 0
BigDecimal('NaN') # => NaN
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, true) # => 2
BigDecimal('NaN') # Raises FloatDomainError
Бесконечности
Режим BigDecimal::EXCEPTION_INFINITY управляет поведением при создании BigDecimal Infinity или -Infinity. Настройки:
-
false(по умолчанию): ВозвращаетBigDecimal('Infinity')илиBigDecimal('-Infinity'). -
true: ВызываетFloatDomainError.
Примеры:
BigDecimal.mode(BigDecimal::EXCEPTION_ALL, false) # => 0
BigDecimal('Infinity') # => Infinity
BigDecimal('-Infinity') # => -Infinity
BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, true) # => 1
BigDecimal('Infinity') # Raises FloatDomainError
BigDecimal('-Infinity') # Raises FloatDomainError
Подпоток
Режим BigDecimal::EXCEPTION_UNDERFLOW управляет поведением при возникновении подпотока BigDecimal. Настройки:
-
false(по умолчанию): ВозвращаетBigDecimal('0')илиBigDecimal('-Infinity'). -
true: ВызываетFloatDomainError.
Примеры:
BigDecimal.mode(BigDecimal::EXCEPTION_ALL, false) # => 0
def flow_under
x = BigDecimal('0.1')
100.times { x *= x }
end
flow_under # => 100
BigDecimal.mode(BigDecimal::EXCEPTION_UNDERFLOW, true) # => 4
flow_under # Raises FloatDomainError
Переполнение
Режим BigDecimal::EXCEPTION_OVERFLOW управляет поведением при возникновении переполнения BigDecimal. Настройки:
-
false(по умолчанию): ВозвращаетBigDecimal('Infinity')илиBigDecimal('-Infinity'). -
true: ВызываетFloatDomainError.
Примеры:
BigDecimal.mode(BigDecimal::EXCEPTION_ALL, false) # => 0
def flow_over
x = BigDecimal('10')
100.times { x *= x }
end
flow_over # => 100
BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, true) # => 1
flow_over # Raises FloatDomainError
Деление на ноль
Режим BigDecimal::EXCEPTION_ZERODIVIDE управляет поведением при делении на ноль. Настройки:
-
false(по умолчанию): ВозвращаетBigDecimal('Infinity')илиBigDecimal('-Infinity'). -
true: ВызываетFloatDomainError.
Примеры:
BigDecimal.mode(BigDecimal::EXCEPTION_ALL, false) # => 0
one = BigDecimal('1')
zero = BigDecimal('0')
one / zero # => Infinity
BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, true) # => 16
one / zero # Raises FloatDomainError
Все исключения
Режим BigDecimal::EXCEPTION_ALL управляет всеми вышеперечисленными:
BigDecimal.mode(BigDecimal::EXCEPTION_ALL, false) # => 0 BigDecimal.mode(BigDecimal::EXCEPTION_ALL, true) # => 23
Округление
Режим BigDecimal::ROUND_MODE управляет способом выполнения округления; его значения:
-
ROUND_UP: Округляет в сторону от нуля. Также известен как:up. -
ROUND_DOWN: Округляет к нулю. Также известен как:downи:truncate. -
ROUND_HALF_UP: Округляет к ближайшему соседу; если соседи равноудалены, округляет в сторону от нуля. Также известен как:half_upи:default. -
ROUND_HALF_DOWN: Округляет к ближайшему соседу; если соседи равноудалены, округляет к нулю. Также известен как:half_down. -
ROUND_HALF_EVEN(округление по правилам банковского округления): Округляет к ближайшему соседу; если соседи равноудалены, округляет к четному соседу. Также известен как:half_evenи:banker. -
ROUND_CEILING: Округляет к положительной бесконечности. Также известен как:ceilingи:ceil. -
ROUND_FLOOR: Округляет к отрицательной бесконечности. Также известен как:floor:.
static VALUE
BigDecimal_save_exception_mode(VALUE self)
{
unsigned short const exception_mode = VpGetException();
int state;
VALUE ret = rb_protect(rb_yield, Qnil, &state);
VpSetException(exception_mode);
if (state) rb_jump_tag(state);
return ret;
} Выполнить предоставленный блок, но сохранить режим исключений
BigDecimal.save_exception_mode do
BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
BigDecimal(BigDecimal('Infinity'))
BigDecimal(BigDecimal('-Infinity'))
BigDecimal(BigDecimal('NaN'))
end
Для использования с BigDecimal::EXCEPTION_*
См. BigDecimal.mode
static VALUE
BigDecimal_save_limit(VALUE self)
{
size_t const limit = VpGetPrecLimit();
int state;
VALUE ret = rb_protect(rb_yield, Qnil, &state);
VpSetPrecLimit(limit);
if (state) rb_jump_tag(state);
return ret;
} Выполнить предоставленный блок, но сохранить предел точности
BigDecimal.limit(100)
puts BigDecimal.limit
BigDecimal.save_limit do
BigDecimal.limit(200)
puts BigDecimal.limit
end
puts BigDecimal.limit
static VALUE
BigDecimal_save_rounding_mode(VALUE self)
{
unsigned short const round_mode = VpGetRoundMode();
int state;
VALUE ret = rb_protect(rb_yield, Qnil, &state);
VpSetRoundMode(round_mode);
if (state) rb_jump_tag(state);
return ret;
} Выполнить предоставленный блок, но сохранить режим округления
BigDecimal.save_rounding_mode do BigDecimal.mode(BigDecimal::ROUND_MODE, :up) puts BigDecimal.mode(BigDecimal::ROUND_MODE) end
Для использования с BigDecimal::ROUND_*
См. BigDecimal.mode
Методы публичного экземпляра
static VALUE BigDecimal_mod(VALUE self, VALUE r)
Возвращает остаток от деления на b.
См. BigDecimal#divmod.
static VALUE
BigDecimal_mult(VALUE self, VALUE r)
{
ENTER(5);
Real *c, *a, *b;
size_t mx;
GUARD_OBJ(a, GetVpValue(self, 1));
if (RB_TYPE_P(r, T_FLOAT)) {
b = GetVpValueWithPrec(r, 0, 1);
}
else if (RB_TYPE_P(r, T_RATIONAL)) {
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
}
else {
b = GetVpValue(r,0);
}
if (!b) return DoSomeOne(self, r, '*');
SAVE(b);
mx = a->Prec + b->Prec;
GUARD_OBJ(c, NewZeroWrapLimited(1, mx * (VpBaseFig() + 1)));
VpMult(c, a, b);
return VpCheckGetValue(c);
} static VALUE
BigDecimal_power_op(VALUE self, VALUE exp)
{
return BigDecimal_power(1, &exp, self);
} Возвращает значение BigDecimal для self в степени other:
b = BigDecimal('3.14')
b ** 2 # => 0.98596e1
b ** 2.0 # => 0.98596e1
b ** Rational(2, 1) # => 0.98596e1
Связанно с: BigDecimal#power.
static VALUE
BigDecimal_add(VALUE self, VALUE r)
{
ENTER(5);
Real *c, *a, *b;
size_t mx;
GUARD_OBJ(a, GetVpValue(self, 1));
if (RB_TYPE_P(r, T_FLOAT)) {
b = GetVpValueWithPrec(r, 0, 1);
}
else if (RB_TYPE_P(r, T_RATIONAL)) {
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
}
else {
b = GetVpValue(r, 0);
}
if (!b) return DoSomeOne(self,r,'+');
SAVE(b);
if (VpIsNaN(b)) return b->obj;
if (VpIsNaN(a)) return a->obj;
mx = GetAddSubPrec(a, b);
if (mx == (size_t)-1L) {
GUARD_OBJ(c, NewZeroWrapLimited(1, VpBaseFig() + 1));
VpAddSub(c, a, b, 1);
}
else {
GUARD_OBJ(c, NewZeroWrapLimited(1, mx * (VpBaseFig() + 1)));
if (!mx) {
VpSetInf(c, VpGetSign(a));
}
else {
VpAddSub(c, a, b, 1);
}
}
return VpCheckGetValue(c);
} Возвращает сумму BigDecimal для self и value:
b = BigDecimal('111111.111') # => 0.111111111e6
b + 2 # => 0.111113111e6
b + 2.0 # => 0.111113111e6
b + Rational(2, 1) # => 0.111113111e6
b + Complex(2, 0) # => (0.111113111e6+0i)
static VALUE
BigDecimal_uplus(VALUE self)
{
return self;
} Возвращает self:
+BigDecimal(5) # => 0.5e1 +BigDecimal(-5) # => -0.5e1
static VALUE
BigDecimal_sub(VALUE self, VALUE r)
{
ENTER(5);
Real *c, *a, *b;
size_t mx;
GUARD_OBJ(a, GetVpValue(self,1));
if (RB_TYPE_P(r, T_FLOAT)) {
b = GetVpValueWithPrec(r, 0, 1);
}
else if (RB_TYPE_P(r, T_RATIONAL)) {
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
}
else {
b = GetVpValue(r,0);
}
if (!b) return DoSomeOne(self,r,'-');
SAVE(b);
if (VpIsNaN(b)) return b->obj;
if (VpIsNaN(a)) return a->obj;
mx = GetAddSubPrec(a,b);
if (mx == (size_t)-1L) {
GUARD_OBJ(c, NewZeroWrapLimited(1, VpBaseFig() + 1));
VpAddSub(c, a, b, -1);
}
else {
GUARD_OBJ(c, NewZeroWrapLimited(1, mx *(VpBaseFig() + 1)));
if (!mx) {
VpSetInf(c,VpGetSign(a));
}
else {
VpAddSub(c, a, b, -1);
}
}
return VpCheckGetValue(c);
} Возвращает разницу BigDecimal для self и value:
b = BigDecimal('333333.333') # => 0.333333333e6
b - 2 # => 0.333331333e6
b - 2.0 # => 0.333331333e6
b - Rational(2, 1) # => 0.333331333e6
b - Complex(2, 0) # => (0.333331333e6+0i)
static VALUE
BigDecimal_neg(VALUE self)
{
ENTER(5);
Real *c, *a;
GUARD_OBJ(a, GetVpValue(self, 1));
GUARD_OBJ(c, NewZeroWrapLimited(1, a->Prec *(VpBaseFig() + 1)));
VpAsgn(c, a, -1);
return VpCheckGetValue(c);
} Возвращает отрицание BigDecimal для self:
b0 = BigDecimal('1.5')
b1 = -b0 # => -0.15e1
b2 = -b1 # => 0.15e1
static VALUE
BigDecimal_div(VALUE self, VALUE r)
/* For c = self/r: with round operation */
{
ENTER(5);
Real *c=NULL, *res=NULL, *div = NULL;
r = BigDecimal_divide(self, r, &c, &res, &div);
if (!NIL_P(r)) return r; /* coerced by other */
SAVE(c); SAVE(res); SAVE(div);
/* a/b = c + r/b */
/* c xxxxx
r 00000yyyyy ==> (y/b)*BASE >= HALF_BASE
*/
/* Round */
if (VpHasVal(div)) { /* frac[0] must be zero for NaN,INF,Zero */
VpInternalRound(c, 0, c->frac[c->Prec-1], (DECDIG)(VpBaseVal() * (DECDIG_DBL)res->frac[0] / div->frac[0]));
}
return VpCheckGetValue(c);
} Деление на указанное значение.
Точность результата будет равна точности большего операнда, но не меньше 2*Float::DIG.
См. BigDecimal#div. См. BigDecimal#quo.
static VALUE
BigDecimal_lt(VALUE self, VALUE r)
{
return BigDecimalCmp(self, r, '<');
} Возвращает true если self меньше other, false в противном случае:
b = BigDecimal('1.5') # => 0.15e1
b < 2 # => true
b < 2.0 # => true
b < Rational(2, 1) # => true
b < 1.5 # => false
Вызывает исключение, если сравнение невозможно.
static VALUE
BigDecimal_le(VALUE self, VALUE r)
{
return BigDecimalCmp(self, r, 'L');
} Возвращает true если self меньше или равно other, false в противном случае:
b = BigDecimal('1.5') # => 0.15e1
b <= 2 # => true
b <= 2.0 # => true
b <= Rational(2, 1) # => true
b <= 1.5 # => true
b < 1 # => false
Вызывает исключение, если сравнение невозможно.
static VALUE
BigDecimal_comp(VALUE self, VALUE r)
{
return BigDecimalCmp(self, r, '*');
} Оператор сравнения. a <=> b равен 0, если a == b, 1, если a > b, -1, если a < b.
static VALUE
BigDecimal_eq(VALUE self, VALUE r)
{
return BigDecimalCmp(self, r, '=');
} Проверка равенства значений; возвращает true, если значения равны.
Операторы == и ===, а также метод eql? имеют одинаковую реализацию для BigDecimal.
Значения могут быть приведены для выполнения сравнения:
BigDecimal('1.0') == 1.0 #=> true
Проверка равенства значений; возвращает true, если значения равны.
Операторы == и ===, а также метод eql? имеют одинаковую реализацию для BigDecimal.
Значения могут быть приведены для выполнения сравнения:
BigDecimal('1.0') == 1.0 #=> true
static VALUE
BigDecimal_gt(VALUE self, VALUE r)
{
return BigDecimalCmp(self, r, '>');
} Возвращает true если self больше other, false в противном случае:
b = BigDecimal('1.5')
b > 1 # => true
b > 1.0 # => true
b > Rational(1, 1) # => true
b > 2 # => false
Вызывает исключение, если сравнение невозможно.
static VALUE
BigDecimal_ge(VALUE self, VALUE r)
{
return BigDecimalCmp(self, r, 'G');
} Возвращает true если self больше или равно other, false в противном случае:
b = BigDecimal('1.5')
b >= 1 # => true
b >= 1.0 # => true
b >= Rational(1, 1) # => true
b >= 1.5 # => true
b > 2 # => false
Вызывает исключение, если сравнение невозможно.
static VALUE
BigDecimal_dump(int argc, VALUE *argv, VALUE self)
{
ENTER(5);
Real *vp;
char *psz;
VALUE dummy;
volatile VALUE dump;
size_t len;
rb_scan_args(argc, argv, "01", &dummy);
GUARD_OBJ(vp,GetVpValue(self, 1));
dump = rb_str_new(0, VpNumOfChars(vp, "E")+50);
psz = RSTRING_PTR(dump);
snprintf(psz, RSTRING_LEN(dump), "%"PRIuSIZE":", VpMaxPrec(vp)*VpBaseFig());
len = strlen(psz);
VpToString(vp, psz+len, RSTRING_LEN(dump)-len, 0, 0);
rb_str_resize(dump, strlen(psz));
return dump;
} Возвращает строку, представляющую маршаллинг self. См. модуль Marshal.
inf = BigDecimal('Infinity') # => Infinity
dumped = inf._dump # => "9:Infinity"
BigDecimal._load(dumped) # => Infinity
static VALUE
BigDecimal_abs(VALUE self)
{
ENTER(5);
Real *c, *a;
size_t mx;
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec *(VpBaseFig() + 1);
GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpAsgn(c, a, 1);
VpChangeSign(c, 1);
return VpCheckGetValue(c);
} Возвращает абсолютное значение BigDecimal для self:
BigDecimal('5').abs # => 0.5e1
BigDecimal('-3').abs # => 0.3e1
static VALUE
BigDecimal_add2(VALUE self, VALUE b, VALUE n)
{
ENTER(2);
Real *cv;
SIGNED_VALUE mx = check_int_precision(n);
if (mx == 0) return BigDecimal_add(self, b);
else {
size_t pl = VpSetPrecLimit(0);
VALUE c = BigDecimal_add(self, b);
VpSetPrecLimit(pl);
GUARD_OBJ(cv, GetVpValue(c, 1));
VpLeftRound(cv, VpGetRoundMode(), mx);
return VpCheckGetValue(cv);
}
} Возвращает сумму BigDecimal для self и value с точностью ndigits десятичных знаков.
Когда ndigits меньше количества значащих цифр в сумме, сумма округляется до этого количества цифр в соответствии с текущим режимом округления; см. BigDecimal.mode.
Примеры:
# Set the rounding mode.
BigDecimal.mode(BigDecimal::ROUND_MODE, :half_up)
b = BigDecimal('111111.111')
b.add(1, 0) # => 0.111112111e6
b.add(1, 3) # => 0.111e6
b.add(1, 6) # => 0.111112e6
b.add(1, 15) # => 0.111112111e6
b.add(1.0, 15) # => 0.111112111e6
b.add(Rational(1, 1), 15) # => 0.111112111e6
# File ext/json/lib/json/add/bigdecimal.rb, line 35
def as_json(*)
{
JSON.create_id => self.class.name,
'b' => _dump,
}
end Методы BigDecimal#as_json и BigDecimal.json_create могут быть использованы для сериализации и десериализации объекта BigDecimal; см. Marshal.
Метод BigDecimal#as_json сериализует self, возвращая хеш из 2 элементов, представляющий self:
require 'json/add/bigdecimal'
x = BigDecimal(2).as_json # => {"json_class"=>"BigDecimal", "b"=>"27:0.2e1"}
y = BigDecimal(2.0, 4).as_json # => {"json_class"=>"BigDecimal", "b"=>"36:0.2e1"}
z = BigDecimal(Complex(2, 0)).as_json # => {"json_class"=>"BigDecimal", "b"=>"27:0.2e1"}
Метод JSON.create десериализует такой хеш, возвращая объект BigDecimal:
BigDecimal.json_create(x) # => 0.2e1 BigDecimal.json_create(y) # => 0.2e1 BigDecimal.json_create(z) # => 0.2e1
static VALUE
BigDecimal_ceil(int argc, VALUE *argv, VALUE self)
{
ENTER(5);
Real *c, *a;
int iLoc;
VALUE vLoc;
size_t mx, pl = VpSetPrecLimit(0);
if (rb_scan_args(argc, argv, "01", &vLoc) == 0) {
iLoc = 0;
} else {
iLoc = NUM2INT(vLoc);
}
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpSetPrecLimit(pl);
VpActiveRound(c, a, VP_ROUND_CEIL, iLoc);
if (argc == 0) {
return BigDecimal_to_i(VpCheckGetValue(c));
}
return VpCheckGetValue(c);
} Возвращает наименьшее целое число, большее или равное значению, в виде BigDecimal.
BigDecimal('3.14159').ceil #=> 4
BigDecimal('-9.1').ceil #=> -9
Если n задано и положительно, дробная часть результата содержит не более указанного количества цифр.
Если n задано и отрицательно, в результате слева от десятичной точки будет по крайней мере столько цифр, равных 0.
BigDecimal('3.14159').ceil(3) #=> 3.142
BigDecimal('13345.234').ceil(-2) #=> 13400.0
static VALUE
BigDecimal_coerce(VALUE self, VALUE other)
{
ENTER(2);
VALUE obj;
Real *b;
if (RB_TYPE_P(other, T_FLOAT)) {
GUARD_OBJ(b, GetVpValueWithPrec(other, 0, 1));
obj = rb_assoc_new(VpCheckGetValue(b), self);
}
else {
if (RB_TYPE_P(other, T_RATIONAL)) {
Real* pv = DATA_PTR(self);
GUARD_OBJ(b, GetVpValueWithPrec(other, pv->Prec*VpBaseFig(), 1));
}
else {
GUARD_OBJ(b, GetVpValue(other, 1));
}
obj = rb_assoc_new(b->obj, self);
}
return obj;
} Метод coerce обеспечивает поддержку приведения типов в Ruby. По умолчанию он не включен.
Это означает, что бинарные операции, такие как +, *, / или -, часто могут выполняться над BigDecimal и объектом другого типа, если другой объект может быть приведен к значению BigDecimal.
Например:
a = BigDecimal("1.0")
b = a / 2.0 #=> 0.5
Обратите внимание, что приведение String к BigDecimal по умолчанию не поддерживается; для этого требуется специальный параметр компиляции при сборке Ruby.
static VALUE
BigDecimal_div3(int argc, VALUE *argv, VALUE self)
{
VALUE b,n;
rb_scan_args(argc, argv, "11", &b, &n);
return BigDecimal_div2(self, b, n);
} Деление на указанное значение.
- digits
-
Если задано и меньше, чем количество значащих цифр результата, результат округляется до этого количества цифр в соответствии с
BigDecimal.mode.Если digits равно 0, результат такой же, как для оператора / или
quo.Если digits не задано, результат — целое число, аналогично
Float#div; см. такжеBigDecimal#divmod.
См. BigDecimal#/. См. BigDecimal#quo.
Примеры:
a = BigDecimal("4")
b = BigDecimal("3")
a.div(b, 3) # => 0.133e1
a.div(b, 0) # => 0.1333333333333333333e1
a / b # => 0.1333333333333333333e1
a.quo(b) # => 0.1333333333333333333e1
a.div(b) # => 1
static VALUE
BigDecimal_divmod(VALUE self, VALUE r)
{
ENTER(5);
Real *div = NULL, *mod = NULL;
if (BigDecimal_DoDivmod(self, r, &div, &mod)) {
SAVE(div); SAVE(mod);
return rb_assoc_new(VpCheckGetValue(div), VpCheckGetValue(mod));
}
return DoSomeOne(self,r,rb_intern("divmod"));
} Делит на заданное значение и возвращает частное и остаток как числа BigDecimal. Частное округляется к отрицательной бесконечности.
Например:
require 'bigdecimal'
a = BigDecimal("42")
b = BigDecimal("9")
q, m = a.divmod(b)
c = q * b + m
a == c #=> true
Частное q равно (a/b).floor, а остаток — величина, которую необходимо добавить к q * b, чтобы получить a.
Проверяет равенство значений; возвращает true, если значения равны.
Операторы == и ===, а также метод eql? имеют одинаковую реализацию для BigDecimal.
Значения могут быть приведены для выполнения сравнения:
BigDecimal('1.0') == 1.0 #=> true
static VALUE
BigDecimal_exponent(VALUE self)
{
ssize_t e = VpExponent10(GetVpValue(self, 1));
return SSIZET2NUM(e);
} Возвращает показатель степени числа BigDecimal как целое число Integer.
Если число можно представить как 0.xxxxxx*10**n, где xxxxxx — строка цифр без ведущих нулей, то n — показатель степени.
static VALUE
BigDecimal_IsFinite(VALUE self)
{
Real *p = GetVpValue(self, 1);
if (VpIsNaN(p)) return Qfalse;
if (VpIsInf(p)) return Qfalse;
return Qtrue;
} Возвращает True, если значение конечно (не NaN и не бесконечно).
static VALUE
BigDecimal_fix(VALUE self)
{
ENTER(5);
Real *c, *a;
size_t mx;
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec *(VpBaseFig() + 1);
GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpActiveRound(c, a, VP_ROUND_DOWN, 0); /* 0: round off */
return VpCheckGetValue(c);
} Возвращает целую часть числа в виде BigDecimal.
static VALUE
BigDecimal_floor(int argc, VALUE *argv, VALUE self)
{
ENTER(5);
Real *c, *a;
int iLoc;
VALUE vLoc;
size_t mx, pl = VpSetPrecLimit(0);
if (rb_scan_args(argc, argv, "01", &vLoc)==0) {
iLoc = 0;
}
else {
iLoc = NUM2INT(vLoc);
}
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpSetPrecLimit(pl);
VpActiveRound(c, a, VP_ROUND_FLOOR, iLoc);
#ifdef BIGDECIMAL_DEBUG
VPrint(stderr, "floor: c=%\n", c);
#endif
if (argc == 0) {
return BigDecimal_to_i(VpCheckGetValue(c));
}
return VpCheckGetValue(c);
} Возвращает наибольшее целое число, меньшее или равное значению, в виде BigDecimal.
BigDecimal('3.14159').floor #=> 3
BigDecimal('-9.1').floor #=> -10
Если n задано и положительно, дробная часть результата содержит не более указанного количества цифр.
Если n задано и отрицательно, в результате слева от десятичной точки будет по крайней мере столько цифр, равных 0.
BigDecimal('3.14159').floor(3) #=> 3.141
BigDecimal('13345.234').floor(-2) #=> 13300.0
static VALUE
BigDecimal_frac(VALUE self)
{
ENTER(5);
Real *c, *a;
size_t mx;
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpFrac(c, a);
return VpCheckGetValue(c);
} Возвращает дробную часть числа в виде BigDecimal.
static VALUE
BigDecimal_hash(VALUE self)
{
ENTER(1);
Real *p;
st_index_t hash;
GUARD_OBJ(p, GetVpValue(self, 1));
hash = (st_index_t)p->sign;
/* hash!=2: the case for 0(1),NaN(0) or +-Infinity(3) is sign itself */
if(hash == 2 || hash == (st_index_t)-2) {
hash ^= rb_memhash(p->frac, sizeof(DECDIG)*p->Prec);
hash += p->exponent;
}
return ST2FIX(hash);
} Возвращает целочисленное значение хэша для self.
Два экземпляра BigDecimal имеют одинаковое значение хэша тогда и только тогда, когда у них равны:
-
Знак.
-
Дробная часть.
-
Показатель степени.
static VALUE
BigDecimal_IsInfinite(VALUE self)
{
Real *p = GetVpValue(self, 1);
if (VpIsPosInf(p)) return INT2FIX(1);
if (VpIsNegInf(p)) return INT2FIX(-1);
return Qnil;
} Возвращает nil, -1 или +1 в зависимости от того, является ли значение конечным, -бесконечностью или +бесконечностью.
static VALUE
BigDecimal_inspect(VALUE self)
{
ENTER(5);
Real *vp;
volatile VALUE str;
size_t nc;
GUARD_OBJ(vp, GetVpValue(self, 1));
nc = VpNumOfChars(vp, "E");
str = rb_str_new(0, nc);
VpToString(vp, RSTRING_PTR(str), RSTRING_LEN(str), 0, 0);
rb_str_resize(str, strlen(RSTRING_PTR(str)));
return str;
} Возвращает строковое представление self.
BigDecimal("1234.5678").inspect
#=> "0.12345678e4"
static VALUE
BigDecimal_mult2(VALUE self, VALUE b, VALUE n)
{
ENTER(2);
Real *cv;
SIGNED_VALUE mx = check_int_precision(n);
if (mx == 0) return BigDecimal_mult(self, b);
else {
size_t pl = VpSetPrecLimit(0);
VALUE c = BigDecimal_mult(self, b);
VpSetPrecLimit(pl);
GUARD_OBJ(cv, GetVpValue(c, 1));
VpLeftRound(cv, VpGetRoundMode(), mx);
return VpCheckGetValue(cv);
}
} Возвращает произведение BigDecimal self и value с точностью до ndigits десятичных цифр.
Когда ndigits меньше количества значащих цифр в сумме, сумма округляется до этого количества цифр в соответствии с текущим режимом округления; см. BigDecimal.mode.
Примеры:
# Set the rounding mode.
BigDecimal.mode(BigDecimal::ROUND_MODE, :half_up)
b = BigDecimal('555555.555')
b.mult(3, 0) # => 0.1666666665e7
b.mult(3, 3) # => 0.167e7
b.mult(3, 6) # => 0.166667e7
b.mult(3, 15) # => 0.1666666665e7
b.mult(3.0, 0) # => 0.1666666665e7
b.mult(Rational(3, 1), 0) # => 0.1666666665e7
b.mult(Complex(3, 0), 0) # => (0.1666666665e7+0.0i)
static VALUE
BigDecimal_n_significant_digits(VALUE self)
{
ENTER(1);
Real *p;
GUARD_OBJ(p, GetVpValue(self, 1));
if (VpIsZero(p) || !VpIsDef(p)) {
return INT2FIX(0);
}
ssize_t n = p->Prec; /* The length of frac without trailing zeros. */
for (n = p->Prec; n > 0 && p->frac[n-1] == 0; --n);
if (n == 0) return INT2FIX(0);
DECDIG x;
int nlz = BASE_FIG;
for (x = p->frac[0]; x > 0; x /= 10) --nlz;
int ntz = 0;
for (x = p->frac[n-1]; x > 0 && x % 10 == 0; x /= 10) ++ntz;
ssize_t n_significant_digits = BASE_FIG*n - nlz - ntz;
return SSIZET2NUM(n_significant_digits);
} Возвращает количество значащих десятичных цифр в self.
BigDecimal("0").n_significant_digits # => 0
BigDecimal("1").n_significant_digits # => 1
BigDecimal("1.1").n_significant_digits # => 2
BigDecimal("3.1415").n_significant_digits # => 5
BigDecimal("-1e20").n_significant_digits # => 1
BigDecimal("1e-20").n_significant_digits # => 1
BigDecimal("Infinity").n_significant_digits # => 0
BigDecimal("-Infinity").n_significant_digits # => 0
BigDecimal("NaN").n_significant_digits # => 0
static VALUE
BigDecimal_IsNaN(VALUE self)
{
Real *p = GetVpValue(self, 1);
if (VpIsNaN(p)) return Qtrue;
return Qfalse;
} Возвращает True, если значение равно Not a Number.
static VALUE
BigDecimal_nonzero(VALUE self)
{
Real *a = GetVpValue(self, 1);
return VpIsZero(a) ? Qnil : self;
} Возвращает self, если значение не равно нулю, в противном случае nil.
static VALUE
BigDecimal_power(int argc, VALUE*argv, VALUE self)
{
ENTER(5);
VALUE vexp, prec;
Real* exp = NULL;
Real *x, *y;
ssize_t mp, ma, n;
SIGNED_VALUE int_exp;
double d;
rb_scan_args(argc, argv, "11", &vexp, &prec);
GUARD_OBJ(x, GetVpValue(self, 1));
n = NIL_P(prec) ? (ssize_t)(x->Prec*VpBaseFig()) : NUM2SSIZET(prec);
if (VpIsNaN(x)) {
y = NewZeroWrapLimited(1, n);
VpSetNaN(y);
RB_GC_GUARD(y->obj);
return VpCheckGetValue(y);
}
retry:
switch (TYPE(vexp)) {
case T_FIXNUM:
break;
case T_BIGNUM:
break;
case T_FLOAT:
d = RFLOAT_VALUE(vexp);
if (d == round(d)) {
if (FIXABLE(d)) {
vexp = LONG2FIX((long)d);
}
else {
vexp = rb_dbl2big(d);
}
goto retry;
}
if (NIL_P(prec)) {
n += BIGDECIMAL_DOUBLE_FIGURES;
}
exp = GetVpValueWithPrec(vexp, 0, 1);
break;
case T_RATIONAL:
if (is_zero(rb_rational_num(vexp))) {
if (is_positive(vexp)) {
vexp = INT2FIX(0);
goto retry;
}
}
else if (is_one(rb_rational_den(vexp))) {
vexp = rb_rational_num(vexp);
goto retry;
}
exp = GetVpValueWithPrec(vexp, n, 1);
if (NIL_P(prec)) {
n += n;
}
break;
case T_DATA:
if (is_kind_of_BigDecimal(vexp)) {
VALUE zero = INT2FIX(0);
VALUE rounded = BigDecimal_round(1, &zero, vexp);
if (RTEST(BigDecimal_eq(vexp, rounded))) {
vexp = BigDecimal_to_i(vexp);
goto retry;
}
if (NIL_P(prec)) {
GUARD_OBJ(y, GetVpValue(vexp, 1));
n += y->Prec*VpBaseFig();
}
exp = DATA_PTR(vexp);
break;
}
/* fall through */
default:
rb_raise(rb_eTypeError,
"wrong argument type %"PRIsVALUE" (expected scalar Numeric)",
RB_OBJ_CLASSNAME(vexp));
}
if (VpIsZero(x)) {
if (is_negative(vexp)) {
y = NewZeroWrapNolimit(1, n);
if (BIGDECIMAL_NEGATIVE_P(x)) {
if (is_integer(vexp)) {
if (is_even(vexp)) {
/* (-0) ** (-even_integer) -> Infinity */
VpSetPosInf(y);
}
else {
/* (-0) ** (-odd_integer) -> -Infinity */
VpSetNegInf(y);
}
}
else {
/* (-0) ** (-non_integer) -> Infinity */
VpSetPosInf(y);
}
}
else {
/* (+0) ** (-num) -> Infinity */
VpSetPosInf(y);
}
RB_GC_GUARD(y->obj);
return VpCheckGetValue(y);
}
else if (is_zero(vexp)) {
return VpCheckGetValue(NewOneWrapLimited(1, n));
}
else {
return VpCheckGetValue(NewZeroWrapLimited(1, n));
}
}
if (is_zero(vexp)) {
return VpCheckGetValue(NewOneWrapLimited(1, n));
}
else if (is_one(vexp)) {
return self;
}
if (VpIsInf(x)) {
if (is_negative(vexp)) {
if (BIGDECIMAL_NEGATIVE_P(x)) {
if (is_integer(vexp)) {
if (is_even(vexp)) {
/* (-Infinity) ** (-even_integer) -> +0 */
return VpCheckGetValue(NewZeroWrapLimited(1, n));
}
else {
/* (-Infinity) ** (-odd_integer) -> -0 */
return VpCheckGetValue(NewZeroWrapLimited(-1, n));
}
}
else {
/* (-Infinity) ** (-non_integer) -> -0 */
return VpCheckGetValue(NewZeroWrapLimited(-1, n));
}
}
else {
return VpCheckGetValue(NewZeroWrapLimited(1, n));
}
}
else {
y = NewZeroWrapLimited(1, n);
if (BIGDECIMAL_NEGATIVE_P(x)) {
if (is_integer(vexp)) {
if (is_even(vexp)) {
VpSetPosInf(y);
}
else {
VpSetNegInf(y);
}
}
else {
/* TODO: support complex */
rb_raise(rb_eMathDomainError,
"a non-integral exponent for a negative base");
}
}
else {
VpSetPosInf(y);
}
return VpCheckGetValue(y);
}
}
if (exp != NULL) {
return bigdecimal_power_by_bigdecimal(x, exp, n);
}
else if (RB_TYPE_P(vexp, T_BIGNUM)) {
VALUE abs_value = BigDecimal_abs(self);
if (is_one(abs_value)) {
return VpCheckGetValue(NewOneWrapLimited(1, n));
}
else if (RTEST(rb_funcall(abs_value, '<', 1, INT2FIX(1)))) {
if (is_negative(vexp)) {
y = NewZeroWrapLimited(1, n);
VpSetInf(y, (is_even(vexp) ? 1 : -1) * VpGetSign(x));
return VpCheckGetValue(y);
}
else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) {
return VpCheckGetValue(NewZeroWrapLimited(-1, n));
}
else {
return VpCheckGetValue(NewZeroWrapLimited(1, n));
}
}
else {
if (is_positive(vexp)) {
y = NewZeroWrapLimited(1, n);
VpSetInf(y, (is_even(vexp) ? 1 : -1) * VpGetSign(x));
return VpCheckGetValue(y);
}
else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) {
return VpCheckGetValue(NewZeroWrapLimited(-1, n));
}
else {
return VpCheckGetValue(NewZeroWrapLimited(1, n));
}
}
}
int_exp = FIX2LONG(vexp);
ma = int_exp;
if (ma < 0) ma = -ma;
if (ma == 0) ma = 1;
if (VpIsDef(x)) {
mp = x->Prec * (VpBaseFig() + 1);
GUARD_OBJ(y, NewZeroWrapLimited(1, mp * (ma + 1)));
}
else {
GUARD_OBJ(y, NewZeroWrapLimited(1, 1));
}
VpPowerByInt(y, x, int_exp);
if (!NIL_P(prec) && VpIsDef(y)) {
VpMidRound(y, VpGetRoundMode(), n);
}
return VpCheckGetValue(y);
} Возвращает значение, возведённое в степень n.
Обратите внимание, что n должно быть Integer.
Также доступно как оператор **.
static VALUE
BigDecimal_precision(VALUE self)
{
ssize_t precision;
BigDecimal_count_precision_and_scale(self, &precision, NULL);
return SSIZET2NUM(precision);
} Возвращает количество десятичных знаков в self:
BigDecimal("0").precision # => 0
BigDecimal("1").precision # => 1
BigDecimal("1.1").precision # => 2
BigDecimal("3.1415").precision # => 5
BigDecimal("-1e20").precision # => 21
BigDecimal("1e-20").precision # => 20
BigDecimal("Infinity").precision # => 0
BigDecimal("-Infinity").precision # => 0
BigDecimal("NaN").precision # => 0
static VALUE
BigDecimal_precision_scale(VALUE self)
{
ssize_t precision, scale;
BigDecimal_count_precision_and_scale(self, &precision, &scale);
return rb_assoc_new(SSIZET2NUM(precision), SSIZET2NUM(scale));
} Возвращает массив длины 2; первый элемент — результат вызова BigDecimal#precision, а второй — результат BigDecimal#scale.
См. BigDecimal#precision. См. BigDecimal#scale.
static VALUE
BigDecimal_prec(VALUE self)
{
ENTER(1);
Real *p;
VALUE obj;
rb_category_warn(RB_WARN_CATEGORY_DEPRECATED,
"BigDecimal#precs is deprecated and will be removed in the future; "
"use BigDecimal#precision instead.");
GUARD_OBJ(p, GetVpValue(self, 1));
obj = rb_assoc_new(SIZET2NUM(p->Prec*VpBaseFig()),
SIZET2NUM(p->MaxPrec*VpBaseFig()));
return obj;
} Возвращает Array из двух Integer значений, которые представляют платформенно-зависимые свойства внутреннего хранения.
Этот метод устарел и будет удалён в будущем. Вместо него используйте BigDecimal#n_significant_digits для получения количества значащих цифр в экспоненциальной форме и BigDecimal#precision для получения количества цифр в десятичной форме.
static VALUE
BigDecimal_quo(int argc, VALUE *argv, VALUE self)
{
VALUE value, digits, result;
SIGNED_VALUE n = -1;
argc = rb_scan_args(argc, argv, "11", &value, &digits);
if (argc > 1) {
n = check_int_precision(digits);
}
if (n > 0) {
result = BigDecimal_div2(self, value, digits);
}
else {
result = BigDecimal_div(self, value);
}
return result;
} Деление на указанное значение.
- digits
-
Если указано и меньше числа значащих цифр результата, результат округляется до заданного количества цифр в соответствии с режимом округления, указанным в
BigDecimal.mode.Если digits равно 0 или опущено, результат такой же, как и для оператора /.
См. BigDecimal#/. См. BigDecimal#div.
static VALUE
BigDecimal_remainder(VALUE self, VALUE r) /* remainder */
{
VALUE f;
Real *d, *rv = 0;
f = BigDecimal_divremain(self, r, &d, &rv);
if (!NIL_P(f)) return f;
return VpCheckGetValue(rv);
} Возвращает остаток от деления на значение.
x.remainder(y) означает x-y*(x/y).truncate
static VALUE
BigDecimal_round(int argc, VALUE *argv, VALUE self)
{
ENTER(5);
Real *c, *a;
int iLoc = 0;
VALUE vLoc;
VALUE vRound;
int round_to_int = 0;
size_t mx, pl;
unsigned short sw = VpGetRoundMode();
switch (rb_scan_args(argc, argv, "02", &vLoc, &vRound)) {
case 0:
iLoc = 0;
round_to_int = 1;
break;
case 1:
if (RB_TYPE_P(vLoc, T_HASH)) {
sw = check_rounding_mode_option(vLoc);
}
else {
iLoc = NUM2INT(vLoc);
if (iLoc < 1) round_to_int = 1;
}
break;
case 2:
iLoc = NUM2INT(vLoc);
if (RB_TYPE_P(vRound, T_HASH)) {
sw = check_rounding_mode_option(vRound);
}
else {
sw = check_rounding_mode(vRound);
}
break;
default:
break;
}
pl = VpSetPrecLimit(0);
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpSetPrecLimit(pl);
VpActiveRound(c, a, sw, iLoc);
if (round_to_int) {
return BigDecimal_to_i(VpCheckGetValue(c));
}
return VpCheckGetValue(c);
} Округление до ближайшего целого (по умолчанию), возвращающее результат как BigDecimal, если n указано, или как Integer, если нет.
BigDecimal('3.14159').round #=> 3
BigDecimal('8.7').round #=> 9
BigDecimal('-9.9').round #=> -10
BigDecimal('3.14159').round(2).class.name #=> "BigDecimal"
BigDecimal('3.14159').round.class.name #=> "Integer"
Если n указано и положительно, дробная часть результата имеет не более указанного количества цифр.
Если n указано и отрицательно, в результате будет как минимум столько нулей слева от десятичной точки, и возвращаемое значение будет Integer.
BigDecimal('3.14159').round(3) #=> 3.142
BigDecimal('13345.234').round(-2) #=> 13300
Значение необязательного аргумента mode может использоваться для определения способа округления; см. BigDecimal.mode.
static VALUE
BigDecimal_scale(VALUE self)
{
ssize_t scale;
BigDecimal_count_precision_and_scale(self, NULL, &scale);
return SSIZET2NUM(scale);
} Возвращает количество десятичных знаков после десятичной точки в self.
BigDecimal("0").scale # => 0
BigDecimal("1").scale # => 0
BigDecimal("1.1").scale # => 1
BigDecimal("3.1415").scale # => 4
BigDecimal("-1e20").precision # => 0
BigDecimal("1e-20").precision # => 20
BigDecimal("Infinity").scale # => 0
BigDecimal("-Infinity").scale # => 0
BigDecimal("NaN").scale # => 0
static VALUE
BigDecimal_sign(VALUE self)
{ /* sign */
int s = GetVpValue(self, 1)->sign;
return INT2FIX(s);
} Возвращает знак значения.
Возвращает положительное значение, если > 0, отрицательное, если < 0. Ведёт себя одинаково с нулями: возвращает положительное значение для положительного нуля (BigDecimal(‘0’)) и отрицательное значение для отрицательного нуля (BigDecimal(‘-0’)).
Конкретное возвращаемое значение указывает тип и знак BigDecimal, как показано ниже:
-
BigDecimal::SIGN_NaN -
значение — Не число
-
BigDecimal::SIGN_POSITIVE_ZERO -
значение — +0
-
BigDecimal::SIGN_NEGATIVE_ZERO -
значение — -0
-
BigDecimal::SIGN_POSITIVE_INFINITE -
значение — +Бесконечность
-
BigDecimal::SIGN_NEGATIVE_INFINITE -
значение — -Бесконечность
-
BigDecimal::SIGN_POSITIVE_FINITE -
значение — положительное
-
BigDecimal::SIGN_NEGATIVE_FINITE -
значение — отрицательное
static VALUE
BigDecimal_split(VALUE self)
{
ENTER(5);
Real *vp;
VALUE obj,str;
ssize_t e, s;
char *psz1;
GUARD_OBJ(vp, GetVpValue(self, 1));
str = rb_str_new(0, VpNumOfChars(vp, "E"));
psz1 = RSTRING_PTR(str);
VpSzMantissa(vp, psz1, RSTRING_LEN(str));
s = 1;
if(psz1[0] == '-') {
size_t len = strlen(psz1 + 1);
memmove(psz1, psz1 + 1, len);
psz1[len] = '\0';
s = -1;
}
if (psz1[0] == 'N') s = 0; /* NaN */
e = VpExponent10(vp);
obj = rb_ary_new2(4);
rb_ary_push(obj, INT2FIX(s));
rb_ary_push(obj, str);
rb_str_resize(str, strlen(psz1));
rb_ary_push(obj, INT2FIX(10));
rb_ary_push(obj, SSIZET2NUM(e));
return obj;
} Разделяет число BigDecimal на четыре части, возвращая их в виде массива значений.
Первое значение представляет знак BigDecimal и равно -1 или 1, или 0, если BigDecimal — Не число.
Второе значение — строка, представляющая значащие цифры BigDecimal без ведущих нулей.
Третье значение — основание, используемое для арифметических операций (в настоящее время всегда 10), как Integer.
Четвёртое значение — показатель степени Integer.
Если BigDecimal может быть представлено как 0.xxxxxx*10**n, то xxxxxx — строка значащих цифр без ведущих нулей, а n — показатель степени.
По этим значениям можно перевести BigDecimal в float следующим образом:
sign, significant_digits, base, exponent = a.split
f = sign * "0.#{significant_digits}".to_f * (base ** exponent)
(Обратите внимание, что метод to_f предоставляется как более удобный способ преобразования BigDecimal в Float.)
static VALUE
BigDecimal_sqrt(VALUE self, VALUE nFig)
{
ENTER(5);
Real *c, *a;
size_t mx, n;
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
n = check_int_precision(nFig);
n += VpDblFig() + VpBaseFig();
if (mx <= n) mx = n;
GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpSqrt(c, a);
return VpCheckGetValue(c);
} Возвращает квадратный корень из значения.
Результат имеет как минимум n значащих цифр.
static VALUE
BigDecimal_sub2(VALUE self, VALUE b, VALUE n)
{
ENTER(2);
Real *cv;
SIGNED_VALUE mx = check_int_precision(n);
if (mx == 0) return BigDecimal_sub(self, b);
else {
size_t pl = VpSetPrecLimit(0);
VALUE c = BigDecimal_sub(self, b);
VpSetPrecLimit(pl);
GUARD_OBJ(cv, GetVpValue(c, 1));
VpLeftRound(cv, VpGetRoundMode(), mx);
return VpCheckGetValue(cv);
}
} Вычитание указанного значения.
Например:
c = a.sub(b,n)
- digits
-
Если указано и меньше числа значащих цифр результата, результат округляется до указанного количества цифр в соответствии с
BigDecimal.mode.
# File ext/bigdecimal/lib/bigdecimal/util.rb, line 110 def to_d self end
Возвращает self.
require 'bigdecimal/util'
d = BigDecimal("3.14")
d.to_d # => 0.314e1
# File ext/bigdecimal/lib/bigdecimal/util.rb, line 90
def to_digits
if self.nan? || self.infinite? || self.zero?
self.to_s
else
i = self.to_i.to_s
_,f,_,z = self.frac.split
i + "." + ("0"*(-z)) + f
end
end Преобразование BigDecimal в строку формата «nnnnnn.mmm». Этот метод устарел; используйте BigDecimal#to_s(“F”) вместо него.
require 'bigdecimal/util'
d = BigDecimal("3.14")
d.to_digits # => "3.14"
static VALUE
BigDecimal_to_f(VALUE self)
{
ENTER(1);
Real *p;
double d;
SIGNED_VALUE e;
char *buf;
volatile VALUE str;
GUARD_OBJ(p, GetVpValue(self, 1));
if (VpVtoD(&d, &e, p) != 1)
return rb_float_new(d);
if (e > (SIGNED_VALUE)(DBL_MAX_10_EXP+BASE_FIG))
goto overflow;
if (e < (SIGNED_VALUE)(DBL_MIN_10_EXP-BASE_FIG))
goto underflow;
str = rb_str_new(0, VpNumOfChars(p, "E"));
buf = RSTRING_PTR(str);
VpToString(p, buf, RSTRING_LEN(str), 0, 0);
errno = 0;
d = strtod(buf, 0);
if (errno == ERANGE) {
if (d == 0.0) goto underflow;
if (fabs(d) >= HUGE_VAL) goto overflow;
}
return rb_float_new(d);
overflow:
VpException(VP_EXCEPTION_OVERFLOW, "BigDecimal to Float conversion", 0);
if (BIGDECIMAL_NEGATIVE_P(p))
return rb_float_new(VpGetDoubleNegInf());
else
return rb_float_new(VpGetDoublePosInf());
underflow:
VpException(VP_EXCEPTION_UNDERFLOW, "BigDecimal to Float conversion", 0);
if (BIGDECIMAL_NEGATIVE_P(p))
return rb_float_new(-0.0);
else
return rb_float_new(0.0);
} Возвращает новый объект Float с приблизительно таким же значением, что и число BigDecimal. Применяются обычные ограничения точности и встроенные ошибки двоичной арифметики Float.
static VALUE
BigDecimal_to_i(VALUE self)
{
ENTER(5);
ssize_t e, nf;
Real *p;
GUARD_OBJ(p, GetVpValue(self, 1));
BigDecimal_check_num(p);
e = VpExponent10(p);
if (e <= 0) return INT2FIX(0);
nf = VpBaseFig();
if (e <= nf) {
return LONG2NUM((long)(VpGetSign(p) * (DECDIG_DBL_SIGNED)p->frac[0]));
}
else {
VALUE a = BigDecimal_split(self);
VALUE digits = RARRAY_AREF(a, 1);
VALUE numerator = rb_funcall(digits, rb_intern("to_i"), 0);
VALUE ret;
ssize_t dpower = e - (ssize_t)RSTRING_LEN(digits);
if (BIGDECIMAL_NEGATIVE_P(p)) {
numerator = rb_funcall(numerator, '*', 1, INT2FIX(-1));
}
if (dpower < 0) {
ret = rb_funcall(numerator, rb_intern("div"), 1,
rb_funcall(INT2FIX(10), rb_intern("**"), 1,
INT2FIX(-dpower)));
}
else {
ret = rb_funcall(numerator, '*', 1,
rb_funcall(INT2FIX(10), rb_intern("**"), 1,
INT2FIX(dpower)));
}
if (RB_TYPE_P(ret, T_FLOAT)) {
rb_raise(rb_eFloatDomainError, "Infinity");
}
return ret;
}
} Возвращает значение в виде Integer.
Если BigDecimal равен бесконечности или NaN, генерирует исключение FloatDomainError.
Возвращает значение в виде Integer.
Если BigDecimal равен бесконечности или NaN, генерирует исключение FloatDomainError.
# File ext/json/lib/json/add/bigdecimal.rb, line 55 def to_json(*args) as_json.to_json(*args) end
Возвращает строку JSON, представляющую self:
require 'json/add/bigdecimal' puts BigDecimal(2).to_json puts BigDecimal(2.0, 4).to_json puts BigDecimal(Complex(2, 0)).to_json
Вывод:
{"json_class":"BigDecimal","b":"27:0.2e1"}
{"json_class":"BigDecimal","b":"36:0.2e1"}
{"json_class":"BigDecimal","b":"27:0.2e1"}
static VALUE
BigDecimal_to_r(VALUE self)
{
Real *p;
ssize_t sign, power, denomi_power;
VALUE a, digits, numerator;
p = GetVpValue(self, 1);
BigDecimal_check_num(p);
sign = VpGetSign(p);
power = VpExponent10(p);
a = BigDecimal_split(self);
digits = RARRAY_AREF(a, 1);
denomi_power = power - RSTRING_LEN(digits);
numerator = rb_funcall(digits, rb_intern("to_i"), 0);
if (sign < 0) {
numerator = rb_funcall(numerator, '*', 1, INT2FIX(-1));
}
if (denomi_power < 0) {
return rb_Rational(numerator,
rb_funcall(INT2FIX(10), rb_intern("**"), 1,
INT2FIX(-denomi_power)));
}
else {
return rb_Rational1(rb_funcall(numerator, '*', 1,
rb_funcall(INT2FIX(10), rb_intern("**"), 1,
INT2FIX(denomi_power))));
}
} Преобразует BigDecimal в Rational.
static VALUE
BigDecimal_to_s(int argc, VALUE *argv, VALUE self)
{
ENTER(5);
int fmt = 0; /* 0: E format, 1: F format */
int fPlus = 0; /* 0: default, 1: set ' ' before digits, 2: set '+' before digits. */
Real *vp;
volatile VALUE str;
char *psz;
char ch;
size_t nc, mc = 0;
SIGNED_VALUE m;
VALUE f;
GUARD_OBJ(vp, GetVpValue(self, 1));
if (rb_scan_args(argc, argv, "01", &f) == 1) {
if (RB_TYPE_P(f, T_STRING)) {
psz = StringValueCStr(f);
if (*psz == ' ') {
fPlus = 1;
psz++;
}
else if (*psz == '+') {
fPlus = 2;
psz++;
}
while ((ch = *psz++) != 0) {
if (ISSPACE(ch)) {
continue;
}
if (!ISDIGIT(ch)) {
if (ch == 'F' || ch == 'f') {
fmt = 1; /* F format */
}
break;
}
mc = mc*10 + ch - '0';
}
}
else {
m = NUM2INT(f);
if (m <= 0) {
rb_raise(rb_eArgError, "argument must be positive");
}
mc = (size_t)m;
}
}
if (fmt) {
nc = VpNumOfChars(vp, "F");
}
else {
nc = VpNumOfChars(vp, "E");
}
if (mc > 0) {
nc += (nc + mc - 1) / mc + 1;
}
str = rb_usascii_str_new(0, nc);
psz = RSTRING_PTR(str);
if (fmt) {
VpToFString(vp, psz, RSTRING_LEN(str), mc, fPlus);
}
else {
VpToString (vp, psz, RSTRING_LEN(str), mc, fPlus);
}
rb_str_resize(str, strlen(psz));
return str;
} Преобразует значение в строку.
Формат по умолчанию похож на 0.xxxxEnn.
Необязательный параметр s состоит либо из целого числа; либо из необязательного «+» или « », за которым необязательно следует число, за которым необязательно следует «E» или «F».
Если в начале s стоит «+», положительные значения возвращаются с ведущим «+».
Пробел в начале s возвращает положительные значения с ведущим пробелом.
Если s содержит число, пробел вставляется после каждой группы из этого числа цифр, начиная с «.» и считая наружу.
Если s заканчивается на «E», используется инженерная нотация (0.xxxxEnn).
Если s заканчивается на «F», используется обычная нотация чисел с плавающей запятой.
Примеры:
BigDecimal('-1234567890123.45678901234567890').to_s('5F')
#=> '-123 45678 90123.45678 90123 45678 9'
BigDecimal('1234567890123.45678901234567890').to_s('+8F')
#=> '+12345 67890123.45678901 23456789'
BigDecimal('1234567890123.45678901234567890').to_s(' F')
#=> ' 1234567890123.4567890123456789'
static VALUE
BigDecimal_truncate(int argc, VALUE *argv, VALUE self)
{
ENTER(5);
Real *c, *a;
int iLoc;
VALUE vLoc;
size_t mx, pl = VpSetPrecLimit(0);
if (rb_scan_args(argc, argv, "01", &vLoc) == 0) {
iLoc = 0;
}
else {
iLoc = NUM2INT(vLoc);
}
GUARD_OBJ(a, GetVpValue(self, 1));
mx = a->Prec * (VpBaseFig() + 1);
GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
VpSetPrecLimit(pl);
VpActiveRound(c, a, VP_ROUND_DOWN, iLoc); /* 0: truncate */
if (argc == 0) {
return BigDecimal_to_i(VpCheckGetValue(c));
}
return VpCheckGetValue(c);
} Округляет до ближайшего целого числа (по умолчанию), возвращая результат как BigDecimal.
BigDecimal('3.14159').truncate #=> 3
BigDecimal('8.7').truncate #=> 8
BigDecimal('-9.9').truncate #=> -9
Если n указан и положителен, дробная часть результата не имеет более, чем указанное количество цифр.
Если n указан и отрицателен, по крайней мере, указанное количество цифр слева от десятичной точки будет равно 0 в результате.
BigDecimal('3.14159').truncate(3) #=> 3.141
BigDecimal('13345.234').truncate(-2) #=> 13300.0
static VALUE
BigDecimal_zero(VALUE self)
{
Real *a = GetVpValue(self, 1);
return VpIsZero(a) ? Qtrue : Qfalse;
} Возвращает True, если значение равно нулю.
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.