класс BigDecimal
BigDecimal предоставляет арифметику с плавающей точкой и произвольной точностью для десятичных чисел.
Введение
Ruby предоставляет встроенную поддержку арифметики с произвольной точностью для целых чисел.
Например:
42**13 #=> 1265437718438866624512
BigDecimal предоставляет аналогичную поддержку для очень больших или очень точных чисел с плавающей точкой.
Десятичная арифметика также полезна для общих вычислений, так как она обеспечивает правильные ответы, ожидаемые пользователем, в то время как обычная арифметика с плавающей точкой по основанию 2 часто вносит незначительные ошибки из-за преобразования между системами счисления по основанию 10 и 2.
Например, попробуйте:
sum = 0 10_000.times do sum = sum + 0.0001 end print sum #=> 0.9999999999999062
и сравните с выводом от:
require 'bigdecimal'
sum = BigDecimal.new("0")
10_000.times do
sum = sum + BigDecimal.new("0.0001")
end
print sum #=> 0.1E1
Аналогично:
(BigDecimal.new("1.2") - BigDecimal("1.0")) == BigDecimal("0.2") #=> true
(1.2 - 1.0) == 0.2 #=> false
Особенности точной десятичной арифметики
Поскольку BigDecimal более точен, чем обычная арифметика с плавающей точкой по основанию 2, он требует некоторых специальных значений.
Бесконечность
BigDecimal иногда должен возвращать бесконечность, например, при делении значения на ноль.
BigDecimal.new("1.0") / BigDecimal.new("0.0") #=> Infinity
BigDecimal.new("-1.0") / BigDecimal.new("0.0") #=> -Infinity
Вы можете представить бесконечные числа для BigDecimal с помощью строк 'Infinity', '+Infinity' и '-Infinity' (регистрозависимые).
Не число
Когда вычисление приводит к неопределённому значению, возвращается специальное значение NaN (для «не числа»).
Пример:
BigDecimal.new("0.0") / BigDecimal.new("0.0") #=> NaN
Вы также можете создать неопределённые значения.
NaN никогда не считается одинаковым ни с каким другим значением, даже с самим собой:
n = BigDecimal.new('NaN')
n == 0.0 #=> false
n == n #=> false
Положительный и отрицательный ноль
Если результат вычисления слишком мал, чтобы быть представлен как BigDecimal в пределах текущих пределов точности, должен быть возвращён ноль.
Если значение, слишком малое для представления, отрицательное, возвращается значение BigDecimal отрицательного нуля.
BigDecimal.new("1.0") / BigDecimal.new("-Infinity") #=> -0.0
Если значение положительное, возвращается значение положительного нуля.
BigDecimal.new("1.0") / BigDecimal.new("Infinity") #=> 0.0
(См. ::mode для указания пределов точности.)
Обратите внимание, что -0.0 и 0.0 считаются одинаковыми для целей сравнения.
Также обратите внимание, что в математике нет особого понятия отрицательного или положительного нуля; истинное математическое нуль не имеет знака.
Лицензия
Авторские права (C) 2002 Shigeo Kobayashi <shigeo@tinyforest.gr.jp>.
Вы можете распространять под условиями либо GNU General Public License, либо Artistic License, как указано в файле README дистрибутива BigDecimal.
Поддерживается mrkn <mrkn@mrkn.jp> и членами ruby-core.
Документировано zzak <zachary@zacharyscott.net>, mathew <meta@pobox.com> и многими другими участниками.
BigDecimal расширяет базовый класс Numeric, предоставляя методы to_digits и to_d.
Когда вы используете BigDecimal в своём приложении, этот метод будет доступен для объектов BigDecimal.
Константы
- BASE
-
Базовое значение, используемое во внутренних вычислениях. В 32-битной системе BASE равно 10000, что указывает на то, что вычисления выполняются группами из 4 цифр. (Если бы оно было больше, BASE**2 не поместилось бы в 32 бита, поэтому вы не могли бы гарантировать, что две группы всегда могли бы быть перемножены без переполнения.)
- EXCEPTION_ALL
-
Определяет, приводят ли переполнение, недополнение или деление на ноль к тому, что будет выброшено исключение. См. ::mode.
- EXCEPTION_INFINITY
-
Определяет, что происходит, когда результат вычисления равен бесконечности. См. ::mode.
- EXCEPTION_NaN
-
Определяет, что происходит, когда результат вычисления — не число (NaN). См. ::mode.
- EXCEPTION_OVERFLOW
-
Определяет, что происходит, когда результат вычисления — переполнение (результат слишком велик для представления). См. ::mode.
- EXCEPTION_UNDERFLOW
-
Определяет, что происходит, когда результат вычисления — недополнение (результат слишком мал для представления). См. ::mode.
- EXCEPTION_ZERODIVIDE
-
Определяет, что происходит при делении на ноль. См. ::mode.
- INFINITY
-
Положительное бесконечное значение.
- NAN
-
Значение «не число».
- ROUND_CEILING
-
Округление до +∞. См. ::mode.
- ROUND_DOWN
-
Указывает, что значения должны быть округлены к нулю. См. ::mode.
- ROUND_FLOOR
-
Округление до -∞. См. ::mode.
- ROUND_HALF_DOWN
-
Указывает, что цифры ≥ 6 должны округляться вверх, остальные вниз. См. ::mode.
- ROUND_HALF_EVEN
-
Округление к ближайшему чётному соседу. См. ::mode.
- ROUND_HALF_UP
-
Указывает, что цифры ≥ 5 должны округляться вверх, остальные вниз. См. ::mode.
- ROUND_MODE
-
Определяет, что происходит, когда результат должен быть округлен для соответствия необходимому количеству значащих цифр. См. ::mode.
- ROUND_UP
-
Указывает, что значения должны быть округлены от нуля. См. ::mode.
- SIGN_NEGATIVE_FINITE
-
Указывает, что значение отрицательное и конечное. См. #sign.
- SIGN_NEGATIVE_INFINITE
-
Указывает, что значение отрицательное и бесконечное. См. #sign.
- SIGN_NEGATIVE_ZERO
-
Указывает, что значение равно -0. См. #sign.
- SIGN_NaN
-
Указывает, что значение — не число. См. #sign.
- SIGN_POSITIVE_FINITE
-
Указывает, что значение положительное и конечное. См. #sign.
- SIGN_POSITIVE_INFINITE
-
Указывает, что значение положительное и бесконечное. См. #sign.
- SIGN_POSITIVE_ZERO
-
Указывает, что значение равно +0. См. #sign.
Методы публичного класса
static VALUE
BigDecimal_load(VALUE self, VALUE str)
{
ENTER(2);
Real *pv;
unsigned char *pch;
unsigned char ch;
unsigned long m=0;
SafeStringValue(str);
pch = (unsigned char *)RSTRING_PTR(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());
} Метод класса ::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;
Check_Type(nFig, T_FIXNUM);
nf = FIX2INT(nFig);
if (nf < 0) {
rb_raise(rb_eArgError, "argument must be positive");
}
VpSetPrecLimit(nf);
}
return nCur;
} Ограничить количество значащих цифр в вновь созданных числах 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);
Check_Type(which, T_FIXNUM);
f = (unsigned long)FIX2INT(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)
static VALUE
BigDecimal_initialize(int argc, VALUE *argv, VALUE self)
{
ENTER(1);
Real *pv = rb_check_typeddata(self, &BigDecimal_data_type);
Real *x;
GUARD_OBJ(x, BigDecimal_new(argc, argv));
if (ToValue(x)) {
pv = VpCopy(pv, x);
}
else {
VpFree(pv);
pv = x;
}
DATA_PTR(self) = pv;
pv->obj = self;
return self;
} Создать новый объект BigDecimal.
- initial
-
Начальное значение в виде целого числа, вещественного числа, рационального числа, BigDecimal или строки.
Если это строка, пробелы игнорируются, а нераспознанные символы завершают значение.
- digits
-
Количество значащих цифр в виде Fixnum. Если опущено или равно 0, количество значащих цифр определяется по начальному значению.
Фактическое количество значащих цифр, используемых в вычислениях, обычно больше, чем указанное число.
Исключения
- TypeError
-
Если тип
initialне является ни Fixnum, ни Bignum, ни Float, ни Rational, ни BigDecimal, возникает это исключение. - TypeError
-
Если
digitsне является Fixnum, возникает это исключение. - ArgumentError
-
Если
initialявляется Float, аdigitsбольше, чем Float::DIG + 1, возникает это исключение. - ArgumentError
-
Если
initialявляется Float или Rational, а значениеdigitsопущено, возникает это исключение.
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.new(BigDecimal('Infinity'))
BigDecimal.new(BigDecimal('-Infinity'))
BigDecimal(BigDecimal.new('NaN'))
end
Для использования с BigDecimal::EXCEPTION_*
См. ::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_*
См. ::mode
static VALUE
BigDecimal_version(VALUE self)
{
/*
* 1.0.0: Ruby 1.8.0
* 1.0.1: Ruby 1.8.1
* 1.1.0: Ruby 1.9.3
*/
return rb_str_new2("1.1.0");
} Возвращает номер версии BigDecimal.
Общедоступные методы экземпляров
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
-
Если указано и меньше количества значащих цифр результата,
результат округляется до этого количества цифр в соответствии с ::mode.
static VALUE
BigDecimal_power_op(VALUE self, VALUE exp)
{
return BigDecimal_power(1, &exp, self);
} Это синоним #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
-
Если указано и меньше количества значащих цифр результата,
результат округляется до этого количества цифр в соответствии с ::mode.
static VALUE
BigDecimal_uplus(VALUE self)
{
return self;
} Возвращает self.
Например:
b = +a # b == a
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, точность результата зависит от точности внутреннего представления на платформе. Поэтому её значение зависит от платформы.
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.
Например:
b = -a b == a * -1
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);
} Разделить на указанное значение.
Например:
c = a.div(b,n)
- digits
-
Если указано и меньше количества значащих цифр результата,
результат округляется до этого количества цифр в соответствии с ::mode.
Если digits равно 0, результат такой же, как у оператора /. Если нет, результат — целое число BigDecimal, аналогично Numeric#div.
Псевдоним quo предоставляется, так как div(value, 0) эквивалентен вычислению частного; см. #divmod.
static VALUE
BigDecimal_lt(VALUE self, VALUE r)
{
return BigDecimalCmp(self, r, '<');
} Возвращает true, если a меньше b.
Значения могут быть приведены к типу для выполнения сравнения (см. ==, #coerce).
static VALUE
BigDecimal_le(VALUE self, VALUE r)
{
return BigDecimalCmp(self, r, 'L');
} Возвращает true, если a меньше или равно b.
Значения могут быть приведены к типу для выполнения сравнения (см. ==, #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.new('1.0') == 1.0 #=> true
static VALUE
BigDecimal_eq(VALUE self, VALUE r)
{
return BigDecimalCmp(self, r, '=');
} Проверка равенства значений; возвращает true, если значения равны.
Операторы == и ===, а также метод eql? имеют одинаковую реализацию для BigDecimal.
Значения могут быть приведены к типу для выполнения сравнения:
BigDecimal.new('1.0') == 1.0 #=> true
static VALUE
BigDecimal_gt(VALUE self, VALUE r)
{
return BigDecimalCmp(self, r, '>');
} Возвращает true, если a больше b.
Значения могут быть приведены к типу для выполнения сравнения (см. ==, #coerce).
static VALUE
BigDecimal_ge(VALUE self, VALUE r)
{
return BigDecimalCmp(self, r, 'G');
} Возвращает true, если a больше или равно b.
Значения могут быть приведены к типу для выполнения сравнения (см. ==, #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.new('Infinity')
=> #<BigDecimal:1e16fa8,'Infinity',9(9)>
BigDecimal._load(inf._dump)
=> #<BigDecimal:1df8dc8,'Infinity',9(9)> См. модуль 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('5').abs -> 5
BigDecimal('-3').abs -> 3
static VALUE
BigDecimal_add2(VALUE self, VALUE b, VALUE n)
{
ENTER(2);
Real *cv;
SIGNED_VALUE mx = GetPositiveInt(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
-
Если указано и меньше количества значащих цифр результата,
результат округляется до этого количества цифр в соответствии с ::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 {
Check_Type(vLoc, T_FIXNUM);
iLoc = FIX2INT(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 указано и отрицательно, слева от десятичной точки результата будет не менее указанного количества нулей.
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, 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 = ::new(“1.0”) b = a / 2.0 -> 0.5
Обратите внимание, что приведение строки к 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);
} 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 = ::new(“42”) b = ::new(“9”)
q,m = a.divmod(b)
c = q * b + m
a == c -> true
Целая часть q равна (a/b).floor, а остаток — это количество, которое необходимо добавить к q * b, чтобы получить a.
static VALUE
BigDecimal_eq(VALUE self, VALUE r)
{
return BigDecimalCmp(self, r, '=');
} Проверяет равенство значений; возвращает true, если значения равны.
Операторы == и ===, а также метод eql? имеют одинаковую реализацию для BigDecimal.
Значения могут быть приведены для выполнения сравнения:
BigDecimal.new('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);
} Возвращает целую часть числа.
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 {
Check_Type(vLoc, T_FIXNUM);
iLoc = FIX2INT(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);
} Возвращает дробную часть числа.
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 INT2FIX(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;
} Возвращает nil, -1 или +1 в зависимости от того, является ли значение конечным, -бесконечностью или +бесконечностью.
static VALUE
BigDecimal_inspect(VALUE self)
{
ENTER(5);
Real *vp;
volatile VALUE obj;
size_t nc;
char *psz, *tmp;
GUARD_OBJ(vp, GetVpValue(self, 1));
nc = VpNumOfChars(vp, "E");
nc += (nc + 9) / 10;
obj = rb_str_new(0, nc+256);
psz = RSTRING_PTR(obj);
sprintf(psz, "#<BigDecimal:%"PRIxVALUE",'", self);
tmp = psz + strlen(psz);
VpToString(vp, tmp, 10, 0);
tmp += strlen(tmp);
sprintf(tmp, "',%"PRIuSIZE"(%"PRIuSIZE")>", VpPrec(vp)*VpBaseFig(), VpMaxPrec(vp)*VpBaseFig());
rb_str_resize(obj, strlen(psz));
return obj;
} Возвращает отладочную информацию о значении в виде строки с перечислением значений в угловых скобках с ведущим #:
::new(“1234.5678”).inspect -> “#<BigDecimal:b7ea1130,'0.12345678E4',8(12)>”
Первая часть — адрес, вторая — значение в виде строки, а последняя часть ss(mm) — текущее количество значащих цифр и максимальное количество значащих цифр соответственно.
static VALUE
BigDecimal_mult2(VALUE self, VALUE b, VALUE n)
{
ENTER(2);
Real *cv;
SIGNED_VALUE mx = GetPositiveInt(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
-
Если указано и меньше, чем количество значащих цифр
результата, результат округляется до этого числа цифр в соответствии с ::mode.
static VALUE
BigDecimal_IsNaN(VALUE self)
{
Real *p = GetVpValue(self, 1);
if (VpIsNaN(p)) return Qtrue;
return Qfalse;
} Возвращает True, если значение — не число
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 (VpGetSign(x) < 0) {
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 (VpGetSign(x) < 0) {
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 (VpGetSign(x) < 0) {
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 (VpGetSign(x) < 0 && 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 (VpGetSign(x) < 0 && 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;
} Возвращает массив из двух целых чисел Integer.
Первое значение — текущее количество значащих цифр в BigDecimal. Второе значение — максимальное количество значащих цифр для BigDecimal.
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);
} Делит на указанное значение.
Например:
c = a.div(b,n)
- digits
-
Если указано и меньше, чем количество значащих цифр
результата, результат округляется до этого числа цифр в соответствии с ::mode.
Если digits равно 0, результат совпадает с оператором /. В противном случае результат — целое число BigDecimal, аналогично Numeric#div.
Псевдоним quo предоставляется, так как div(value, 0) эквивалентен вычислению частного; см. #divmod.
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:
Check_Type(vLoc, T_FIXNUM);
iLoc = FIX2INT(vLoc);
break;
case 2:
Check_Type(vLoc, T_FIXNUM);
iLoc = FIX2INT(vLoc);
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);
} Округляет до ближайшего 1 (по умолчанию), возвращая результат как BigDecimal.
BigDecimal('3.14159').round #=> 3
BigDecimal('8.7').round #=> 9
Если n задано и положительно, дробная часть результата содержит не более указанного количества цифр.
Если n задано и отрицательно, слева от десятичной точки в результате будет по крайней мере столько нулей.
BigDecimal('3.14159').round(3) #=> 3.142
BigDecimal('13345.234').round(-2) #=> 13300.0
Значение необязательного аргумента mode может использоваться для определения способа округления; см. ::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 в 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 = GetPositiveInt(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 = GetPositiveInt(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);
}
} sub(value, digits) -> bigdecimal
Вычитает указанное значение.
Например:
c = a.sub(b,n)
- digits
-
Если указано и меньше, чем количество значащих цифр результата,
результат округляется до этого количества цифр в соответствии с ::mode.
# File ext/bigdecimal/lib/bigdecimal/util.rb, line 96 def to_d self end
Возвращает self.
# File ext/bigdecimal/lib/bigdecimal/util.rb, line 82
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». Этот метод устарел; используйте #to_s(“F”) вместо него.
require 'bigdecimal'
require 'bigdecimal/util'
d = BigDecimal.new("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 (p->sign >= 0)
return rb_float_new(VpGetDoublePosInf());
else
return rb_float_new(VpGetDoubleNegInf());
underflow:
VpException(VP_EXCEPTION_UNDERFLOW, "BigDecimal to Float conversion", 0);
if (p->sign >= 0)
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) * (BDIGIT_DBL_SIGNED)p->frac[0]));
}
else {
VALUE a = BigDecimal_split(self);
VALUE digits = RARRAY_PTR(a)[1];
VALUE numerator = rb_funcall(digits, rb_intern("to_i"), 0);
VALUE ret;
ssize_t dpower = e - (ssize_t)RSTRING_LEN(digits);
if (VpGetSign(p) < 0) {
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;
}
} Возвращает значение как целое число (Fixnum или Bignum).
Если BigNumber — бесконечность или 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_PTR(a)[1];
VALUE numerator = rb_funcall(digits, rb_intern("to_i"), 0);
VALUE ret;
ssize_t dpower = e - (ssize_t)RSTRING_LEN(digits);
if (VpGetSign(p) < 0) {
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;
}
} Возвращает значение как целое число (Fixnum или Bignum).
Если BigNumber — бесконечность или NaN, генерируется FloatDomainError.
# File ext/json/lib/json/add/bigdecimal.rb, line 25 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_PTR(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 */
int fPlus = 0; /* =0:default,=1: set ' ' before digits ,set '+' before digits. */
Real *vp;
volatile VALUE str;
char *psz;
char ch;
size_t nc, mc = 0;
VALUE f;
GUARD_OBJ(vp, GetVpValue(self, 1));
if (rb_scan_args(argc, argv, "01", &f) == 1) {
if (RB_TYPE_P(f, T_STRING)) {
SafeStringValue(f);
psz = RSTRING_PTR(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 {
mc = (size_t)GetPositiveInt(f);
}
}
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.new('-123.45678901234567890').to_s('5F')
#=> '-123.45678 90123 45678 9'
BigDecimal.new('123.45678901234567890').to_s('+8F')
#=> '+123.45678901 23456789'
BigDecimal.new('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 {
Check_Type(vLoc, T_FIXNUM);
iLoc = FIX2INT(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
Если 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.