| // Written in the D programming language. |
| |
| /** |
| This is a submodule of $(MREF std, math). |
| |
| It contains several functions for rounding floating point numbers. |
| |
| Copyright: Copyright The D Language Foundation 2000 - 2011. |
| D implementations of floor, ceil, and lrint functions are based on the |
| CEPHES math library, which is Copyright (C) 2001 Stephen L. Moshier |
| $(LT)steve@moshier.net$(GT) and are incorporated herein by permission |
| of the author. The author reserves the right to distribute this |
| material elsewhere under different copying permissions. |
| These modifications are distributed here under the following terms: |
| License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). |
| Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston, |
| Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger |
| Source: $(PHOBOSSRC std/math/rounding.d) |
| */ |
| |
| /* NOTE: This file has been patched from the original DMD distribution to |
| * work with the GDC compiler. |
| */ |
| module std.math.rounding; |
| |
| static import core.math; |
| static import core.stdc.math; |
| |
| import std.traits : isFloatingPoint, isIntegral, Unqual; |
| |
| version (D_InlineAsm_X86) version = InlineAsm_X86_Any; |
| version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any; |
| |
| version (InlineAsm_X86_Any) version = InlineAsm_X87; |
| version (InlineAsm_X87) |
| { |
| static assert(real.mant_dig == 64); |
| version (CRuntime_Microsoft) version = InlineAsm_X87_MSVC; |
| } |
| |
| /************************************** |
| * Returns the value of x rounded upward to the next integer |
| * (toward positive infinity). |
| */ |
| real ceil(real x) @trusted pure nothrow @nogc |
| { |
| version (InlineAsm_X87_MSVC) |
| { |
| version (X86_64) |
| { |
| asm pure nothrow @nogc |
| { |
| naked ; |
| fld real ptr [RCX] ; |
| fstcw 8[RSP] ; |
| mov AL,9[RSP] ; |
| mov DL,AL ; |
| and AL,0xC3 ; |
| or AL,0x08 ; // round to +infinity |
| mov 9[RSP],AL ; |
| fldcw 8[RSP] ; |
| frndint ; |
| mov 9[RSP],DL ; |
| fldcw 8[RSP] ; |
| ret ; |
| } |
| } |
| else |
| { |
| short cw; |
| asm pure nothrow @nogc |
| { |
| fld x ; |
| fstcw cw ; |
| mov AL,byte ptr cw+1 ; |
| mov DL,AL ; |
| and AL,0xC3 ; |
| or AL,0x08 ; // round to +infinity |
| mov byte ptr cw+1,AL ; |
| fldcw cw ; |
| frndint ; |
| mov byte ptr cw+1,DL ; |
| fldcw cw ; |
| } |
| } |
| } |
| else |
| { |
| import std.math.traits : isInfinity, isNaN; |
| |
| // Special cases. |
| if (isNaN(x) || isInfinity(x)) |
| return x; |
| |
| real y = floorImpl(x); |
| if (y < x) |
| y += 1.0; |
| |
| return y; |
| } |
| } |
| |
| /// |
| @safe pure nothrow @nogc unittest |
| { |
| import std.math.traits : isNaN; |
| |
| assert(ceil(+123.456L) == +124); |
| assert(ceil(-123.456L) == -123); |
| assert(ceil(-1.234L) == -1); |
| assert(ceil(-0.123L) == 0); |
| assert(ceil(0.0L) == 0); |
| assert(ceil(+0.123L) == 1); |
| assert(ceil(+1.234L) == 2); |
| assert(ceil(real.infinity) == real.infinity); |
| assert(isNaN(ceil(real.nan))); |
| assert(isNaN(ceil(real.init))); |
| } |
| |
| /// ditto |
| double ceil(double x) @trusted pure nothrow @nogc |
| { |
| import std.math.traits : isInfinity, isNaN; |
| |
| // Special cases. |
| if (isNaN(x) || isInfinity(x)) |
| return x; |
| |
| double y = floorImpl(x); |
| if (y < x) |
| y += 1.0; |
| |
| return y; |
| } |
| |
| @safe pure nothrow @nogc unittest |
| { |
| import std.math.traits : isNaN; |
| |
| assert(ceil(+123.456) == +124); |
| assert(ceil(-123.456) == -123); |
| assert(ceil(-1.234) == -1); |
| assert(ceil(-0.123) == 0); |
| assert(ceil(0.0) == 0); |
| assert(ceil(+0.123) == 1); |
| assert(ceil(+1.234) == 2); |
| assert(ceil(double.infinity) == double.infinity); |
| assert(isNaN(ceil(double.nan))); |
| assert(isNaN(ceil(double.init))); |
| } |
| |
| /// ditto |
| float ceil(float x) @trusted pure nothrow @nogc |
| { |
| import std.math.traits : isInfinity, isNaN; |
| |
| // Special cases. |
| if (isNaN(x) || isInfinity(x)) |
| return x; |
| |
| float y = floorImpl(x); |
| if (y < x) |
| y += 1.0; |
| |
| return y; |
| } |
| |
| @safe pure nothrow @nogc unittest |
| { |
| import std.math.traits : isNaN; |
| |
| assert(ceil(+123.456f) == +124); |
| assert(ceil(-123.456f) == -123); |
| assert(ceil(-1.234f) == -1); |
| assert(ceil(-0.123f) == 0); |
| assert(ceil(0.0f) == 0); |
| assert(ceil(+0.123f) == 1); |
| assert(ceil(+1.234f) == 2); |
| assert(ceil(float.infinity) == float.infinity); |
| assert(isNaN(ceil(float.nan))); |
| assert(isNaN(ceil(float.init))); |
| } |
| |
| /************************************** |
| * Returns the value of x rounded downward to the next integer |
| * (toward negative infinity). |
| */ |
| real floor(real x) @trusted pure nothrow @nogc |
| { |
| version (InlineAsm_X87_MSVC) |
| { |
| version (X86_64) |
| { |
| asm pure nothrow @nogc |
| { |
| naked ; |
| fld real ptr [RCX] ; |
| fstcw 8[RSP] ; |
| mov AL,9[RSP] ; |
| mov DL,AL ; |
| and AL,0xC3 ; |
| or AL,0x04 ; // round to -infinity |
| mov 9[RSP],AL ; |
| fldcw 8[RSP] ; |
| frndint ; |
| mov 9[RSP],DL ; |
| fldcw 8[RSP] ; |
| ret ; |
| } |
| } |
| else |
| { |
| short cw; |
| asm pure nothrow @nogc |
| { |
| fld x ; |
| fstcw cw ; |
| mov AL,byte ptr cw+1 ; |
| mov DL,AL ; |
| and AL,0xC3 ; |
| or AL,0x04 ; // round to -infinity |
| mov byte ptr cw+1,AL ; |
| fldcw cw ; |
| frndint ; |
| mov byte ptr cw+1,DL ; |
| fldcw cw ; |
| } |
| } |
| } |
| else |
| { |
| import std.math.traits : isInfinity, isNaN; |
| |
| // Special cases. |
| if (isNaN(x) || isInfinity(x) || x == 0.0) |
| return x; |
| |
| return floorImpl(x); |
| } |
| } |
| |
| /// |
| @safe pure nothrow @nogc unittest |
| { |
| import std.math.traits : isNaN; |
| |
| assert(floor(+123.456L) == +123); |
| assert(floor(-123.456L) == -124); |
| assert(floor(+123.0L) == +123); |
| assert(floor(-124.0L) == -124); |
| assert(floor(-1.234L) == -2); |
| assert(floor(-0.123L) == -1); |
| assert(floor(0.0L) == 0); |
| assert(floor(+0.123L) == 0); |
| assert(floor(+1.234L) == 1); |
| assert(floor(real.infinity) == real.infinity); |
| assert(isNaN(floor(real.nan))); |
| assert(isNaN(floor(real.init))); |
| } |
| |
| /// ditto |
| double floor(double x) @trusted pure nothrow @nogc |
| { |
| import std.math.traits : isInfinity, isNaN; |
| |
| // Special cases. |
| if (isNaN(x) || isInfinity(x) || x == 0.0) |
| return x; |
| |
| return floorImpl(x); |
| } |
| |
| @safe pure nothrow @nogc unittest |
| { |
| import std.math.traits : isNaN; |
| |
| assert(floor(+123.456) == +123); |
| assert(floor(-123.456) == -124); |
| assert(floor(+123.0) == +123); |
| assert(floor(-124.0) == -124); |
| assert(floor(-1.234) == -2); |
| assert(floor(-0.123) == -1); |
| assert(floor(0.0) == 0); |
| assert(floor(+0.123) == 0); |
| assert(floor(+1.234) == 1); |
| assert(floor(double.infinity) == double.infinity); |
| assert(isNaN(floor(double.nan))); |
| assert(isNaN(floor(double.init))); |
| } |
| |
| /// ditto |
| float floor(float x) @trusted pure nothrow @nogc |
| { |
| import std.math.traits : isInfinity, isNaN; |
| |
| // Special cases. |
| if (isNaN(x) || isInfinity(x) || x == 0.0) |
| return x; |
| |
| return floorImpl(x); |
| } |
| |
| @safe pure nothrow @nogc unittest |
| { |
| import std.math.traits : isNaN; |
| |
| assert(floor(+123.456f) == +123); |
| assert(floor(-123.456f) == -124); |
| assert(floor(+123.0f) == +123); |
| assert(floor(-124.0f) == -124); |
| assert(floor(-1.234f) == -2); |
| assert(floor(-0.123f) == -1); |
| assert(floor(0.0f) == 0); |
| assert(floor(+0.123f) == 0); |
| assert(floor(+1.234f) == 1); |
| assert(floor(float.infinity) == float.infinity); |
| assert(isNaN(floor(float.nan))); |
| assert(isNaN(floor(float.init))); |
| } |
| |
| // https://issues.dlang.org/show_bug.cgi?id=6381 |
| // floor/ceil should be usable in pure function. |
| @safe pure nothrow unittest |
| { |
| auto x = floor(1.2); |
| auto y = ceil(1.2); |
| } |
| |
| /** |
| * Round `val` to a multiple of `unit`. `rfunc` specifies the rounding |
| * function to use; by default this is `rint`, which uses the current |
| * rounding mode. |
| */ |
| Unqual!F quantize(alias rfunc = rint, F)(const F val, const F unit) |
| if (is(typeof(rfunc(F.init)) : F) && isFloatingPoint!F) |
| { |
| import std.math.traits : isInfinity; |
| |
| typeof(return) ret = val; |
| if (unit != 0) |
| { |
| const scaled = val / unit; |
| if (!scaled.isInfinity) |
| ret = rfunc(scaled) * unit; |
| } |
| return ret; |
| } |
| |
| /// |
| @safe pure nothrow @nogc unittest |
| { |
| import std.math.operations : isClose; |
| |
| assert(isClose(12345.6789L.quantize(0.01L), 12345.68L)); |
| assert(isClose(12345.6789L.quantize!floor(0.01L), 12345.67L)); |
| assert(isClose(12345.6789L.quantize(22.0L), 12342.0L)); |
| } |
| |
| /// |
| @safe pure nothrow @nogc unittest |
| { |
| import std.math.operations : isClose; |
| import std.math.traits : isNaN; |
| |
| assert(isClose(12345.6789L.quantize(0), 12345.6789L)); |
| assert(12345.6789L.quantize(real.infinity).isNaN); |
| assert(12345.6789L.quantize(real.nan).isNaN); |
| assert(real.infinity.quantize(0.01L) == real.infinity); |
| assert(real.infinity.quantize(real.nan).isNaN); |
| assert(real.nan.quantize(0.01L).isNaN); |
| assert(real.nan.quantize(real.infinity).isNaN); |
| assert(real.nan.quantize(real.nan).isNaN); |
| } |
| |
| /** |
| * Round `val` to a multiple of `pow(base, exp)`. `rfunc` specifies the |
| * rounding function to use; by default this is `rint`, which uses the |
| * current rounding mode. |
| */ |
| Unqual!F quantize(real base, alias rfunc = rint, F, E)(const F val, const E exp) |
| if (is(typeof(rfunc(F.init)) : F) && isFloatingPoint!F && isIntegral!E) |
| { |
| import std.math.exponential : pow; |
| |
| // TODO: Compile-time optimization for power-of-two bases? |
| return quantize!rfunc(val, pow(cast(F) base, exp)); |
| } |
| |
| /// ditto |
| Unqual!F quantize(real base, long exp = 1, alias rfunc = rint, F)(const F val) |
| if (is(typeof(rfunc(F.init)) : F) && isFloatingPoint!F) |
| { |
| import std.math.exponential : pow; |
| |
| enum unit = cast(F) pow(base, exp); |
| return quantize!rfunc(val, unit); |
| } |
| |
| /// |
| @safe pure nothrow @nogc unittest |
| { |
| import std.math.operations : isClose; |
| |
| assert(isClose(12345.6789L.quantize!10(-2), 12345.68L)); |
| assert(isClose(12345.6789L.quantize!(10, -2), 12345.68L)); |
| assert(isClose(12345.6789L.quantize!(10, floor)(-2), 12345.67L)); |
| assert(isClose(12345.6789L.quantize!(10, -2, floor), 12345.67L)); |
| |
| assert(isClose(12345.6789L.quantize!22(1), 12342.0L)); |
| assert(isClose(12345.6789L.quantize!22, 12342.0L)); |
| } |
| |
| @safe pure nothrow @nogc unittest |
| { |
| import std.math.exponential : log10, pow; |
| import std.math.operations : isClose; |
| import std.meta : AliasSeq; |
| |
| static foreach (F; AliasSeq!(real, double, float)) |
| {{ |
| const maxL10 = cast(int) F.max.log10.floor; |
| const maxR10 = pow(cast(F) 10, maxL10); |
| assert(isClose((cast(F) 0.9L * maxR10).quantize!10(maxL10), maxR10)); |
| assert(isClose((cast(F)-0.9L * maxR10).quantize!10(maxL10), -maxR10)); |
| |
| assert(F.max.quantize(F.min_normal) == F.max); |
| assert((-F.max).quantize(F.min_normal) == -F.max); |
| assert(F.min_normal.quantize(F.max) == 0); |
| assert((-F.min_normal).quantize(F.max) == 0); |
| assert(F.min_normal.quantize(F.min_normal) == F.min_normal); |
| assert((-F.min_normal).quantize(F.min_normal) == -F.min_normal); |
| }} |
| } |
| |
| /****************************************** |
| * Rounds x to the nearest integer value, using the current rounding |
| * mode. |
| * |
| * Unlike the rint functions, nearbyint does not raise the |
| * FE_INEXACT exception. |
| */ |
| pragma(inline, true) |
| real nearbyint(real x) @safe pure nothrow @nogc |
| { |
| return core.stdc.math.nearbyintl(x); |
| } |
| |
| /// |
| @safe pure unittest |
| { |
| import std.math.traits : isNaN; |
| |
| assert(nearbyint(0.4) == 0); |
| assert(nearbyint(0.5) == 0); |
| assert(nearbyint(0.6) == 1); |
| assert(nearbyint(100.0) == 100); |
| |
| assert(isNaN(nearbyint(real.nan))); |
| assert(nearbyint(real.infinity) == real.infinity); |
| assert(nearbyint(-real.infinity) == -real.infinity); |
| } |
| |
| /********************************** |
| * Rounds x to the nearest integer value, using the current rounding |
| * mode. |
| * |
| * If the return value is not equal to x, the FE_INEXACT |
| * exception is raised. |
| * |
| * $(LREF nearbyint) performs the same operation, but does |
| * not set the FE_INEXACT exception. |
| */ |
| pragma(inline, true) |
| real rint(real x) @safe pure nothrow @nogc |
| { |
| return core.math.rint(x); |
| } |
| ///ditto |
| pragma(inline, true) |
| double rint(double x) @safe pure nothrow @nogc |
| { |
| return core.math.rint(x); |
| } |
| ///ditto |
| pragma(inline, true) |
| float rint(float x) @safe pure nothrow @nogc |
| { |
| return core.math.rint(x); |
| } |
| |
| /// |
| @safe unittest |
| { |
| import std.math.traits : isNaN; |
| |
| version (IeeeFlagsSupport) resetIeeeFlags(); |
| assert(rint(0.4) == 0); |
| version (GNU) { /* inexact bit not set with enabled optimizations */ } else |
| version (IeeeFlagsSupport) assert(ieeeFlags.inexact); |
| |
| assert(rint(0.5) == 0); |
| assert(rint(0.6) == 1); |
| assert(rint(100.0) == 100); |
| |
| assert(isNaN(rint(real.nan))); |
| assert(rint(real.infinity) == real.infinity); |
| assert(rint(-real.infinity) == -real.infinity); |
| } |
| |
| @safe unittest |
| { |
| real function(real) print = &rint; |
| assert(print != null); |
| } |
| |
| /*************************************** |
| * Rounds x to the nearest integer value, using the current rounding |
| * mode. |
| * |
| * This is generally the fastest method to convert a floating-point number |
| * to an integer. Note that the results from this function |
| * depend on the rounding mode, if the fractional part of x is exactly 0.5. |
| * If using the default rounding mode (ties round to even integers) |
| * lrint(4.5) == 4, lrint(5.5)==6. |
| */ |
| long lrint(real x) @trusted pure nothrow @nogc |
| { |
| version (InlineAsm_X87) |
| { |
| version (Win64) |
| { |
| asm pure nothrow @nogc |
| { |
| naked; |
| fld real ptr [RCX]; |
| fistp qword ptr 8[RSP]; |
| mov RAX,8[RSP]; |
| ret; |
| } |
| } |
| else |
| { |
| long n; |
| asm pure nothrow @nogc |
| { |
| fld x; |
| fistp n; |
| } |
| return n; |
| } |
| } |
| else |
| { |
| import std.math.traits : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB; |
| |
| alias F = floatTraits!(real); |
| static if (F.realFormat == RealFormat.ieeeDouble) |
| { |
| long result; |
| |
| // Rounding limit when casting from real(double) to ulong. |
| enum real OF = 4.50359962737049600000E15L; |
| |
| uint* vi = cast(uint*)(&x); |
| |
| // Find the exponent and sign |
| uint msb = vi[MANTISSA_MSB]; |
| uint lsb = vi[MANTISSA_LSB]; |
| int exp = ((msb >> 20) & 0x7ff) - 0x3ff; |
| const int sign = msb >> 31; |
| msb &= 0xfffff; |
| msb |= 0x100000; |
| |
| if (exp < 63) |
| { |
| if (exp >= 52) |
| result = (cast(long) msb << (exp - 20)) | (lsb << (exp - 52)); |
| else |
| { |
| // Adjust x and check result. |
| const real j = sign ? -OF : OF; |
| x = (j + x) - j; |
| msb = vi[MANTISSA_MSB]; |
| lsb = vi[MANTISSA_LSB]; |
| exp = ((msb >> 20) & 0x7ff) - 0x3ff; |
| msb &= 0xfffff; |
| msb |= 0x100000; |
| |
| if (exp < 0) |
| result = 0; |
| else if (exp < 20) |
| result = cast(long) msb >> (20 - exp); |
| else if (exp == 20) |
| result = cast(long) msb; |
| else |
| result = (cast(long) msb << (exp - 20)) | (lsb >> (52 - exp)); |
| } |
| } |
| else |
| { |
| // It is left implementation defined when the number is too large. |
| return cast(long) x; |
| } |
| |
| return sign ? -result : result; |
| } |
| else static if (F.realFormat == RealFormat.ieeeExtended || |
| F.realFormat == RealFormat.ieeeExtended53) |
| { |
| long result; |
| |
| // Rounding limit when casting from real(80-bit) to ulong. |
| static if (F.realFormat == RealFormat.ieeeExtended) |
| enum real OF = 9.22337203685477580800E18L; |
| else |
| enum real OF = 4.50359962737049600000E15L; |
| |
| ushort* vu = cast(ushort*)(&x); |
| uint* vi = cast(uint*)(&x); |
| |
| // Find the exponent and sign |
| int exp = (vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff; |
| const int sign = (vu[F.EXPPOS_SHORT] >> 15) & 1; |
| |
| if (exp < 63) |
| { |
| // Adjust x and check result. |
| const real j = sign ? -OF : OF; |
| x = (j + x) - j; |
| exp = (vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff; |
| |
| version (LittleEndian) |
| { |
| if (exp < 0) |
| result = 0; |
| else if (exp <= 31) |
| result = vi[1] >> (31 - exp); |
| else |
| result = (cast(long) vi[1] << (exp - 31)) | (vi[0] >> (63 - exp)); |
| } |
| else |
| { |
| if (exp < 0) |
| result = 0; |
| else if (exp <= 31) |
| result = vi[1] >> (31 - exp); |
| else |
| result = (cast(long) vi[1] << (exp - 31)) | (vi[2] >> (63 - exp)); |
| } |
| } |
| else |
| { |
| // It is left implementation defined when the number is too large |
| // to fit in a 64bit long. |
| return cast(long) x; |
| } |
| |
| return sign ? -result : result; |
| } |
| else static if (F.realFormat == RealFormat.ieeeQuadruple) |
| { |
| const vu = cast(ushort*)(&x); |
| |
| // Find the exponent and sign |
| const sign = (vu[F.EXPPOS_SHORT] >> 15) & 1; |
| if ((vu[F.EXPPOS_SHORT] & F.EXPMASK) - (F.EXPBIAS + 1) > 63) |
| { |
| // The result is left implementation defined when the number is |
| // too large to fit in a 64 bit long. |
| return cast(long) x; |
| } |
| |
| // Force rounding of lower bits according to current rounding |
| // mode by adding ±2^-112 and subtracting it again. |
| enum OF = 5.19229685853482762853049632922009600E33L; |
| const j = sign ? -OF : OF; |
| x = (j + x) - j; |
| |
| const exp = (vu[F.EXPPOS_SHORT] & F.EXPMASK) - (F.EXPBIAS + 1); |
| const implicitOne = 1UL << 48; |
| auto vl = cast(ulong*)(&x); |
| vl[MANTISSA_MSB] &= implicitOne - 1; |
| vl[MANTISSA_MSB] |= implicitOne; |
| |
| long result; |
| |
| if (exp < 0) |
| result = 0; |
| else if (exp <= 48) |
| result = vl[MANTISSA_MSB] >> (48 - exp); |
| else |
| result = (vl[MANTISSA_MSB] << (exp - 48)) | (vl[MANTISSA_LSB] >> (112 - exp)); |
| |
| return sign ? -result : result; |
| } |
| else |
| { |
| static assert(false, "real type not supported by lrint()"); |
| } |
| } |
| } |
| |
| /// |
| @safe pure nothrow @nogc unittest |
| { |
| assert(lrint(4.5) == 4); |
| assert(lrint(5.5) == 6); |
| assert(lrint(-4.5) == -4); |
| assert(lrint(-5.5) == -6); |
| |
| assert(lrint(int.max - 0.5) == 2147483646L); |
| assert(lrint(int.max + 0.5) == 2147483648L); |
| assert(lrint(int.min - 0.5) == -2147483648L); |
| assert(lrint(int.min + 0.5) == -2147483648L); |
| } |
| |
| static if (real.mant_dig >= long.sizeof * 8) |
| { |
| @safe pure nothrow @nogc unittest |
| { |
| assert(lrint(long.max - 1.5L) == long.max - 1); |
| assert(lrint(long.max - 0.5L) == long.max - 1); |
| assert(lrint(long.min + 0.5L) == long.min); |
| assert(lrint(long.min + 1.5L) == long.min + 2); |
| } |
| } |
| |
| /******************************************* |
| * Return the value of x rounded to the nearest integer. |
| * If the fractional part of x is exactly 0.5, the return value is |
| * rounded away from zero. |
| * |
| * Returns: |
| * A `real`. |
| */ |
| auto round(real x) @trusted nothrow @nogc |
| { |
| version (CRuntime_Microsoft) |
| { |
| import std.math.hardware : FloatingPointControl; |
| |
| auto old = FloatingPointControl.getControlState(); |
| FloatingPointControl.setControlState( |
| (old & (-1 - FloatingPointControl.roundingMask)) | FloatingPointControl.roundToZero |
| ); |
| x = core.math.rint((x >= 0) ? x + 0.5 : x - 0.5); |
| FloatingPointControl.setControlState(old); |
| return x; |
| } |
| else |
| { |
| return core.stdc.math.roundl(x); |
| } |
| } |
| |
| /// |
| @safe nothrow @nogc unittest |
| { |
| assert(round(4.5) == 5); |
| assert(round(5.4) == 5); |
| assert(round(-4.5) == -5); |
| assert(round(-5.1) == -5); |
| } |
| |
| // assure purity on Posix |
| version (Posix) |
| { |
| @safe pure nothrow @nogc unittest |
| { |
| assert(round(4.5) == 5); |
| } |
| } |
| |
| /********************************************** |
| * Return the value of x rounded to the nearest integer. |
| * |
| * If the fractional part of x is exactly 0.5, the return value is rounded |
| * away from zero. |
| */ |
| long lround(real x) @trusted nothrow @nogc |
| { |
| return core.stdc.math.llroundl(x); |
| } |
| |
| /// |
| @safe nothrow @nogc unittest |
| { |
| assert(lround(0.49) == 0); |
| assert(lround(0.5) == 1); |
| assert(lround(1.5) == 2); |
| } |
| |
| /** |
| Returns the integer portion of x, dropping the fractional portion. |
| This is also known as "chop" rounding. |
| `pure` on all platforms. |
| */ |
| real trunc(real x) @trusted nothrow @nogc pure |
| { |
| version (InlineAsm_X87_MSVC) |
| { |
| version (X86_64) |
| { |
| asm pure nothrow @nogc |
| { |
| naked ; |
| fld real ptr [RCX] ; |
| fstcw 8[RSP] ; |
| mov AL,9[RSP] ; |
| mov DL,AL ; |
| and AL,0xC3 ; |
| or AL,0x0C ; // round to 0 |
| mov 9[RSP],AL ; |
| fldcw 8[RSP] ; |
| frndint ; |
| mov 9[RSP],DL ; |
| fldcw 8[RSP] ; |
| ret ; |
| } |
| } |
| else |
| { |
| short cw; |
| asm pure nothrow @nogc |
| { |
| fld x ; |
| fstcw cw ; |
| mov AL,byte ptr cw+1 ; |
| mov DL,AL ; |
| and AL,0xC3 ; |
| or AL,0x0C ; // round to 0 |
| mov byte ptr cw+1,AL ; |
| fldcw cw ; |
| frndint ; |
| mov byte ptr cw+1,DL ; |
| fldcw cw ; |
| } |
| } |
| } |
| else |
| { |
| return core.stdc.math.truncl(x); |
| } |
| } |
| |
| /// |
| @safe pure unittest |
| { |
| assert(trunc(0.01) == 0); |
| assert(trunc(0.49) == 0); |
| assert(trunc(0.5) == 0); |
| assert(trunc(1.5) == 1); |
| } |
| |
| /***************************************** |
| * Returns x rounded to a long value using the current rounding mode. |
| * If the integer value of x is |
| * greater than long.max, the result is |
| * indeterminate. |
| */ |
| pragma(inline, true) |
| long rndtol(real x) @nogc @safe pure nothrow { return core.math.rndtol(x); } |
| //FIXME |
| ///ditto |
| pragma(inline, true) |
| long rndtol(double x) @safe pure nothrow @nogc { return rndtol(cast(real) x); } |
| //FIXME |
| ///ditto |
| pragma(inline, true) |
| long rndtol(float x) @safe pure nothrow @nogc { return rndtol(cast(real) x); } |
| |
| /// |
| @safe unittest |
| { |
| assert(rndtol(1.0) == 1L); |
| assert(rndtol(1.2) == 1L); |
| assert(rndtol(1.7) == 2L); |
| assert(rndtol(1.0001) == 1L); |
| } |
| |
| @safe unittest |
| { |
| long function(real) prndtol = &rndtol; |
| assert(prndtol != null); |
| } |
| |
| // Helper for floor/ceil |
| T floorImpl(T)(const T x) @trusted pure nothrow @nogc |
| { |
| import std.math.traits : floatTraits, RealFormat; |
| |
| alias F = floatTraits!(T); |
| // Take care not to trigger library calls from the compiler, |
| // while ensuring that we don't get defeated by some optimizers. |
| union floatBits |
| { |
| T rv; |
| ushort[T.sizeof/2] vu; |
| |
| // Other kinds of extractors for real formats. |
| static if (F.realFormat == RealFormat.ieeeSingle) |
| uint vi; |
| else static if (F.realFormat == RealFormat.ieeeDouble) |
| ulong vi; |
| } |
| floatBits y = void; |
| y.rv = x; |
| |
| // Find the exponent (power of 2) |
| // Do this by shifting the raw value so that the exponent lies in the low bits, |
| // then mask out the sign bit, and subtract the bias. |
| static if (F.realFormat == RealFormat.ieeeSingle) |
| { |
| int exp = ((y.vi >> (T.mant_dig - 1)) & 0xff) - 0x7f; |
| enum mantissa_mask = F.MANTISSAMASK_INT; |
| enum sign_shift = 31; |
| } |
| else static if (F.realFormat == RealFormat.ieeeDouble) |
| { |
| long exp = ((y.vi >> (T.mant_dig - 1)) & 0x7ff) - 0x3ff; |
| enum mantissa_mask = F.MANTISSAMASK_LONG; |
| enum sign_shift = 63; |
| } |
| else static if (F.realFormat == RealFormat.ieeeExtended || |
| F.realFormat == RealFormat.ieeeExtended53) |
| { |
| int exp = (y.vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff; |
| |
| version (LittleEndian) |
| int pos = 0; |
| else |
| int pos = 4; |
| } |
| else static if (F.realFormat == RealFormat.ieeeQuadruple) |
| { |
| int exp = (y.vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff; |
| |
| version (LittleEndian) |
| int pos = 0; |
| else |
| int pos = 7; |
| } |
| else |
| static assert(false, "Not implemented for this architecture"); |
| |
| if (exp < 0) |
| { |
| if (x < 0.0) |
| return -1.0; |
| else |
| return 0.0; |
| } |
| |
| static if (F.realFormat == RealFormat.ieeeSingle || |
| F.realFormat == RealFormat.ieeeDouble) |
| { |
| if (exp < (T.mant_dig - 1)) |
| { |
| // Clear all bits representing the fraction part. |
| // Note: the fraction mask represents the floating point number 0.999999... |
| // i.e: `2.0 ^^ (exp - T.mant_dig + 1) * (fraction_mask + 1) == 1.0` |
| const fraction_mask = mantissa_mask >> exp; |
| |
| if ((y.vi & fraction_mask) != 0) |
| { |
| // If 'x' is negative, then first substract (1.0 - T.epsilon) from the value. |
| if (y.vi >> sign_shift) |
| y.vi += fraction_mask; |
| y.vi &= ~fraction_mask; |
| } |
| } |
| } |
| else |
| { |
| static if (F.realFormat == RealFormat.ieeeExtended53) |
| exp = (T.mant_dig + 11 - 1) - exp; // mant_dig is really 64 |
| else |
| exp = (T.mant_dig - 1) - exp; |
| |
| // Zero 16 bits at a time. |
| while (exp >= 16) |
| { |
| version (LittleEndian) |
| y.vu[pos++] = 0; |
| else |
| y.vu[pos--] = 0; |
| exp -= 16; |
| } |
| |
| // Clear the remaining bits. |
| if (exp > 0) |
| y.vu[pos] &= 0xffff ^ ((1 << exp) - 1); |
| |
| if ((x < 0.0) && (x != y.rv)) |
| y.rv -= 1.0; |
| } |
| |
| return y.rv; |
| } |