класс 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 более точен, чем обычная бинарная арифметика с плавающей точкой, он требует некоторых специальных значений.
Бесконечность
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 никогда не считается одинаковым ни с каким другим значением, даже с самим 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
Лицензия
Авторское право (C) 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);
rb_check_safe_obj(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));
m /= VpBaseFig();
if (m && pv->MaxPrec > m) {
pv->MaxPrec = m+1;
}
return ToValue(pv);
} Внутренний метод, используемый для поддержки маршалинга. См. модуль Marshal.
static VALUE
BigDecimal_double_fig(VALUE self)
{
return INT2FIX(VpDblFig());
} Метод класса BigDecimal.double_fig возвращает количество значащих цифр, которое разрешено для числа Float. Результат зависит от используемого процессора и операционной системы.
static VALUE
BigDecimal_limit(int argc, VALUE *argv, VALUE self)
{
VALUE nFig;
VALUE nCur = INT2NUM(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;
} Управляет обработкой исключений арифметики и округления. Если значение не указано, возвращается текущее значение.
Шесть значений параметра mode управляют обработкой арифметических исключений:
BigDecimal::EXCEPTION_NaN BigDecimal::EXCEPTION_INFINITY BigDecimal::EXCEPTION_UNDERFLOW BigDecimal::EXCEPTION_OVERFLOW BigDecimal::EXCEPTION_ZERODIVIDE BigDecimal::EXCEPTION_ALL
Для каждого параметра mode, если установленное значение — false, вычисление продолжается после возникновения арифметического исключения соответствующего типа. При продолжении вычислений результаты следующие:
-
EXCEPTION_NaN -
NaN
-
EXCEPTION_INFINITY -
+Бесконечность или -Бесконечность
-
EXCEPTION_UNDERFLOW -
0
-
EXCEPTION_OVERFLOW -
+Бесконечность или -Бесконечность
-
EXCEPTION_ZERODIVIDE -
+Бесконечность или -Бесконечность
Одно значение параметра mode управляет округлением числовых значений: 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 -
округлить к положительной бесконечности (ceil)
-
ROUND_FLOOR, :floor -
округлить к отрицательной бесконечности (floor)
# File ext/bigdecimal/lib/bigdecimal.rb, line 3 def BigDecimal.new(*args, **kwargs) warn "BigDecimal.new is deprecated; use BigDecimal() method instead.", uplevel: 1 BigDecimal(*args, **kwargs) end
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, DBL_DIG+1, 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, VpCreateRbObject(mx *(VpBaseFig() + 1), "0"));
VpMult(c, a, b);
return ToValue(c);
} Умножить на указанное значение.
Например:
c = a.mult(b,n) c = a * b
- digits
-
Если указано и меньше, чем количество значащих цифр результата, результат округляется до этого количества цифр в соответствии с
BigDecimal.mode.
static VALUE
BigDecimal_power_op(VALUE self, VALUE exp)
{
return BigDecimal_power(1, &exp, self);
} Возвращает значение, возведённое в степень n.
См. 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, DBL_DIG+1, 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,VpCreateRbObject(VpBaseFig() + 1, "0"));
VpAddSub(c, a, b, 1);
}
else {
GUARD_OBJ(c, VpCreateRbObject(mx * (VpBaseFig() + 1), "0"));
if(!mx) {
VpSetInf(c, VpGetSign(a));
}
else {
VpAddSub(c, a, b, 1);
}
}
return ToValue(c);
} Добавить указанное значение.
Например:
c = a.add(b,n) c = a + b
- digits
-
Если указано и меньше, чем количество значащих цифр результата, результат округляется до этого количества цифр в соответствии с
BigDecimal.mode.
static VALUE
BigDecimal_uplus(VALUE self)
{
return self;
} Возвращает self.
+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, DBL_DIG+1, 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,VpCreateRbObject(VpBaseFig() + 1, "0"));
VpAddSub(c, a, b, -1);
}
else {
GUARD_OBJ(c,VpCreateRbObject(mx *(VpBaseFig() + 1), "0"));
if (!mx) {
VpSetInf(c,VpGetSign(a));
}
else {
VpAddSub(c, a, b, -1);
}
}
return ToValue(c);
} Вычесть указанное значение.
Например:
c = a - b
Точность значения результата зависит от типа b.
Если b является Float, точность результата составляет Float::DIG+1.
Если b является BigDecimal, точность результата — это точность внутренней представления b с платформы. Таким образом, значение возврата зависит от платформы.
static VALUE
BigDecimal_neg(VALUE self)
{
ENTER(5);
Real *c, *a;
GUARD_OBJ(a, GetVpValue(self, 1));
GUARD_OBJ(c, VpCreateRbObject(a->Prec *(VpBaseFig() + 1), "0"));
VpAsgn(c, a, -1);
return ToValue(c);
} Возвращает отрицание self.
-BigDecimal('5') #=> -0.5e1
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(&c, &res, &div, self, r);
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], (BDIGIT)(VpBaseVal() * (BDIGIT_DBL)res->frac[0] / div->frac[0]));
}
return ToValue(c);
} Разделить на указанное значение.
См. BigDecimal#div.
static VALUE
BigDecimal_lt(VALUE self, VALUE r)
{
return BigDecimalCmp(self, r, '<');
} Возвращает true, если a меньше b.
Значения могут быть приведены к типу для выполнения сравнения (см. ==, BigDecimal#coerce).
static VALUE
BigDecimal_le(VALUE self, VALUE r)
{
return BigDecimalCmp(self, r, 'L');
} Возвращает true, если a меньше или равно b.
Значения могут быть приведены к типу для выполнения сравнения (см. ==, BigDecimal#coerce).
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
static VALUE
BigDecimal_eq(VALUE self, VALUE r)
{
return BigDecimalCmp(self, r, '=');
} Проверяет равенство значений; возвращает true, если значения равны.
Операторы == и ===, а также метод eql? имеют одинаковую реализацию для BigDecimal.
Значения могут быть приведены к типу для выполнения сравнения:
BigDecimal('1.0') == 1.0 #=> true
static VALUE
BigDecimal_gt(VALUE self, VALUE r)
{
return BigDecimalCmp(self, r, '>');
} Возвращает true, если a больше b.
Значения могут быть приведены к типу для выполнения сравнения (см. ==, BigDecimal#coerce).
static VALUE
BigDecimal_ge(VALUE self, VALUE r)
{
return BigDecimalCmp(self, r, 'G');
} Возвращает true, если a больше или равно b.
Значения могут быть приведены к типу для выполнения сравнения (см. ==, BigDecimal#coerce)
static VALUE
BigDecimal_dump(int argc, VALUE *argv, VALUE self)
{
ENTER(5);
Real *vp;
char *psz;
VALUE dummy;
volatile VALUE dump;
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);
sprintf(psz, "%"PRIuSIZE":", VpMaxPrec(vp)*VpBaseFig());
VpToString(vp, psz+strlen(psz), 0, 0);
rb_str_resize(dump, strlen(psz));
return dump;
} Метод, используемый для поддержки маршализации.
inf = BigDecimal('Infinity')
#=> Infinity
BigDecimal._load(inf._dump)
#=> Infinity
См. модуль Marshal.
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, VpCreateRbObject(mx, "0"));
VpAsgn(c, a, 1);
VpChangeSign(c, 1);
return ToValue(c);
} Возвращает абсолютное значение в виде BigDecimal.
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 = GetPrecisionInt(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 ToValue(cv);
}
} Добавить указанное значение.
Например:
c = a.add(b,n) c = a + b
- digits
-
Если указано и меньше, чем количество значащих цифр результата, результат округляется до этого количества цифр в соответствии с
BigDecimal.mode.
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, VpCreateRbObject(mx, "0"));
VpSetPrecLimit(pl);
VpActiveRound(c, a, VP_ROUND_CEIL, iLoc);
if (argc == 0) {
return BigDecimal_to_i(ToValue(c));
}
return ToValue(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_clone(VALUE self)
{
return self;
} 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, DBL_DIG+1, 1));
obj = rb_assoc_new(ToValue(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.
Примеры:
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(ToValue(div), ToValue(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.
static VALUE
BigDecimal_clone(VALUE self)
{
return self;
} static VALUE
BigDecimal_eq(VALUE self, VALUE r)
{
return BigDecimalCmp(self, r, '=');
} Проверяет равенство значений; возвращает true, если значения равны.
Операторы == и ===, а также метод eql? имеют одинаковую реализацию для BigDecimal.
Значения могут быть приведены для выполнения сравнения:
BigDecimal('1.0') == 1.0 #=> true
static VALUE
BigDecimal_exponent(VALUE self)
{
ssize_t e = VpExponent10(GetVpValue(self, 1));
return INT2NUM(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, VpCreateRbObject(mx, "0"));
VpActiveRound(c, a, VP_ROUND_DOWN, 0); /* 0: round off */
return ToValue(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, VpCreateRbObject(mx, "0"));
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(ToValue(c));
}
return ToValue(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, VpCreateRbObject(mx, "0"));
VpFrac(c, a);
return ToValue(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(BDIGIT)*p->Prec);
hash += p->exponent;
}
return ST2FIX(hash);
} Создаёт хеш для этого BigDecimal.
Два BigDecimals с одинаковым знаком, дробной частью и порядком имеют одинаковый хеш.
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;
} Возвращает null, -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), 0, 0);
rb_str_resize(str, strlen(RSTRING_PTR(str)));
return str;
} Возвращает строковое представление self.
BigDecimal("1234.5678").inspect
#=> "0.12345678e4"
static VALUE BigDecimal_mod(VALUE self, VALUE r)
Возвращает остаток от деления на b.
См. BigDecimal#divmod.
static VALUE
BigDecimal_mult2(VALUE self, VALUE b, VALUE n)
{
ENTER(2);
Real *cv;
SIGNED_VALUE mx = GetPrecisionInt(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 ToValue(cv);
}
} Умножение на заданное значение.
Например:
c = a.mult(b,n) c = a * b
- digits
-
Если указано и меньше, чем количество значащих цифр результата, результат округляется до этого количества цифр в соответствии с
BigDecimal.mode.
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 = VpCreateRbObject(n, "0");
RB_GC_GUARD(y->obj);
VpSetNaN(y);
return ToValue(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;
}
exp = GetVpValueWithPrec(vexp, DBL_DIG+1, 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);
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;
}
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 = VpCreateRbObject(n, "#0");
RB_GC_GUARD(y->obj);
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);
}
return ToValue(y);
}
else if (is_zero(vexp)) {
return ToValue(VpCreateRbObject(n, "1"));
}
else {
return ToValue(VpCreateRbObject(n, "0"));
}
}
if (is_zero(vexp)) {
return ToValue(VpCreateRbObject(n, "1"));
}
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 ToValue(VpCreateRbObject(n, "0"));
}
else {
/* (-Infinity) ** (-odd_integer) -> -0 */
return ToValue(VpCreateRbObject(n, "-0"));
}
}
else {
/* (-Infinity) ** (-non_integer) -> -0 */
return ToValue(VpCreateRbObject(n, "-0"));
}
}
else {
return ToValue(VpCreateRbObject(n, "0"));
}
}
else {
y = VpCreateRbObject(n, "0");
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 ToValue(y);
}
}
if (exp != NULL) {
return rmpd_power_by_big_decimal(x, exp, n);
}
else if (RB_TYPE_P(vexp, T_BIGNUM)) {
VALUE abs_value = BigDecimal_abs(self);
if (is_one(abs_value)) {
return ToValue(VpCreateRbObject(n, "1"));
}
else if (RTEST(rb_funcall(abs_value, '<', 1, INT2FIX(1)))) {
if (is_negative(vexp)) {
y = VpCreateRbObject(n, "0");
if (is_even(vexp)) {
VpSetInf(y, VpGetSign(x));
}
else {
VpSetInf(y, -VpGetSign(x));
}
return ToValue(y);
}
else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) {
return ToValue(VpCreateRbObject(n, "-0"));
}
else {
return ToValue(VpCreateRbObject(n, "0"));
}
}
else {
if (is_positive(vexp)) {
y = VpCreateRbObject(n, "0");
if (is_even(vexp)) {
VpSetInf(y, VpGetSign(x));
}
else {
VpSetInf(y, -VpGetSign(x));
}
return ToValue(y);
}
else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) {
return ToValue(VpCreateRbObject(n, "-0"));
}
else {
return ToValue(VpCreateRbObject(n, "0"));
}
}
}
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, VpCreateRbObject(mp * (ma + 1), "0"));
}
else {
GUARD_OBJ(y, VpCreateRbObject(1, "0"));
}
VpPower(y, x, int_exp);
if (!NIL_P(prec) && VpIsDef(y)) {
VpMidRound(y, VpGetRoundMode(), n);
}
return ToValue(y);
} Возвращает значение, возведённое в степень n.
Обратите внимание, что n должно быть целым числом Integer.
Также доступен в виде оператора **.
static VALUE
BigDecimal_prec(VALUE self)
{
ENTER(1);
Real *p;
VALUE obj;
GUARD_OBJ(p, GetVpValue(self, 1));
obj = rb_assoc_new(INT2NUM(p->Prec*VpBaseFig()),
INT2NUM(p->MaxPrec*VpBaseFig()));
return obj;
} Возвращает массив из двух целых чисел Array.
Первое значение — текущее количество значащих цифр в BigDecimal. Второе значение — максимальное количество значащих цифр для BigDecimal.
BigDecimal('5').precs #=> [9, 18]
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(&c, &res, &div, self, r);
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], (BDIGIT)(VpBaseVal() * (BDIGIT_DBL)res->frac[0] / div->frac[0]));
}
return ToValue(c);
} Деление на указанное значение.
См. 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 ToValue(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;
size_t mx, pl;
unsigned short sw = VpGetRoundMode();
switch (rb_scan_args(argc, argv, "02", &vLoc, &vRound)) {
case 0:
iLoc = 0;
break;
case 1:
if (RB_TYPE_P(vLoc, T_HASH)) {
sw = check_rounding_mode_option(vLoc);
}
else {
iLoc = NUM2INT(vLoc);
}
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, VpCreateRbObject(mx, "0"));
VpSetPrecLimit(pl);
VpActiveRound(c, a, sw, iLoc);
if (argc == 0) {
return BigDecimal_to_i(ToValue(c));
}
return ToValue(c);
} Округляет до ближайшего целого числа (по умолчанию), возвращая результат как BigDecimal.
BigDecimal('3.14159').round #=> 3
BigDecimal('8.7').round #=> 9
BigDecimal('-9.9').round #=> -10
Если n указано и положительно, дробная часть результата содержит не более указанного количества цифр.
Если n указано и отрицательно, слева от десятичной точки в результате будет как минимум указанное количество нулей.
BigDecimal('3.14159').round(3) #=> 3.142
BigDecimal('13345.234').round(-2) #=> 13300.0
Значение необязательного аргумента mode может использоваться для определения способа округления; см. BigDecimal.mode.
static VALUE
BigDecimal_sign(VALUE self)
{ /* sign */
int s = GetVpValue(self, 1)->sign;
return INT2FIX(s);
} Возвращает знак значения.
Возвращает положительное значение, если > 0, отрицательное значение, если < 0, и ноль, если == 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);
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, INT2NUM(e));
return obj;
} Разделяет число BigDecimal на четыре части, возвращая их как массив значений.
Первое значение представляет знак числа BigDecimal и равно -1 или 1, или 0, если BigDecimal — Не число.
Второе значение — строка, представляющая значащие цифры числа BigDecimal без ведущих нулей.
Третье значение — основание, используемое для арифметики (в настоящее время всегда 10), как Integer.
Четвёртое значение — показатель степени Integer.
Если BigDecimal может быть представлено как 0.xxxxxx*10**n, то xxxxxx — строка значащих цифр без ведущих нулей, а n — показатель степени.
По этим значениям вы можете преобразовать BigDecimal в число с плавающей точкой следующим образом:
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 = GetPrecisionInt(nFig) + VpDblFig() + BASE_FIG;
if (mx <= n) mx = n;
GUARD_OBJ(c, VpCreateRbObject(mx, "0"));
VpSqrt(c, a);
return ToValue(c);
} Возвращает квадратный корень из значения.
Результат имеет как минимум n значащих цифр.
static VALUE
BigDecimal_sub2(VALUE self, VALUE b, VALUE n)
{
ENTER(2);
Real *cv;
SIGNED_VALUE mx = GetPrecisionInt(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 ToValue(cv);
}
} Вычитает указанное значение.
Например:
c = a.sub(b,n)
- digits
-
Если указано и меньше количества значащих цифр результата, результат округляется до указанного количества цифр в соответствии с
BigDecimal.mode.
# File ext/bigdecimal/lib/bigdecimal/util.rb, line 104 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 84
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, 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. Действуют обычные ограничения точности и встроенные ошибки арифметики с плавающей точкой.
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) * (BDIGIT_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.
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) * (BDIGIT_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.
# File ext/json/lib/json/add/bigdecimal.rb, line 26 def to_json(*) as_json.to_json end
Возвращает значение JSON.
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);
rb_check_safe_obj(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_str_new(0, nc);
psz = RSTRING_PTR(str);
if (fmt) {
VpToFString(vp, psz, mc, fPlus);
}
else {
VpToString (vp, psz, 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('-123.45678901234567890').to_s('5F')
#=> '-123.45678 90123 45678 9'
BigDecimal('123.45678901234567890').to_s('+8F')
#=> '+123.45678901 23456789'
BigDecimal('123.45678901234567890').to_s(' F')
#=> ' 123.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, VpCreateRbObject(mx, "0"));
VpSetPrecLimit(pl);
VpActiveRound(c, a, VP_ROUND_DOWN, iLoc); /* 0: truncate */
if (argc == 0) {
return BigDecimal_to_i(ToValue(c));
}
return ToValue(c);
} Округляет до ближайшего целого числа (по умолчанию), возвращая результат как BigDecimal.
BigDecimal('3.14159').truncate #=> 3
BigDecimal('8.7').truncate #=> 8
BigDecimal('-9.9').truncate #=> -9
Если n указано и положительно, дробная часть результата содержит не более указанного количества цифр.
Если n указано и отрицательно, слева от десятичной точки в результате будет как минимум указанное количество нулей.
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–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.