com.rojoma.grisu.Grisu.scala Maven / Gradle / Ivy
// Copyright 2010 the V8 project authors. All rights reserved.
// Copyright 2011-2012, Kevin Ring. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.rojoma.grisu
import java.io.{StringWriter, Writer}
import spire.math.ULong
import spire.math.UInt
/** Fast `Double` to text conversion. */
object Grisu {
private val ts_decimal_rep = new ThreadLocal[Array[Char]] {
override protected def initialValue = new Array[Char](kBase10MaximalLength + 1)
}
private class IntBoxes {
var a: Int = _
var b: Int = _
}
/** Convert a `Double` to a `String`. */
def toString(value: Double): String = {
val sw = new StringWriter(kBase10MaximalLength + 3) // room for signs, decimal points, and exponents
toWriter(sw, value)
sw.toString
}
/** Write a `Double` into a `Writer`. */
def toWriter(writer: Writer, value: Double) {
if (value < 0.0) {
writer.write('-')
return toWriter(writer, -value)
}
val grisuDouble = new GrisuDouble(value)
if (grisuDouble.isSpecial) {
return handleSpecialValues(grisuDouble, writer)
}
val decimal_rep = ts_decimal_rep.get()
val intBoxes = new IntBoxes
if (!doubleToShortestAscii(grisuDouble, decimal_rep, intBoxes)) {
return writer.write(value.toString)
}
val decimal_rep_length = intBoxes.a
val decimal_point = intBoxes.b
var decimalRepLength = decimal_rep_length
if (decimal_point < 1) {
decimalRepLength += -decimal_point + 1
} else if (decimal_point >= decimal_rep_length) {
decimalRepLength += decimal_point - decimal_rep_length + 1
}
val exponent = decimal_point - 1
val absExponent = Math.abs(exponent)
var exponentRepLength = decimal_rep_length + 3
if (exponent < 0) exponentRepLength += 1
if (absExponent >= 10) {
exponentRepLength += 1
if (absExponent >= 100) exponentRepLength += 1
}
if (decimalRepLength <= exponentRepLength) {
createDecimalRepresentation(decimal_rep, decimal_rep_length,
decimal_point,
Math.max(0, decimal_rep_length - decimal_point),
writer)
} else {
createExponentialRepresentation(decimal_rep, decimal_rep_length, exponent,
writer)
}
}
// The maximal number of digits that are needed to emit a double in base 10.
// A higher precision can be achieved by using more digits, but the shortest
// accurate representation of any double will never use more digits than
// kBase10MaximalLength.
// Note that DoubleToAscii null-terminates its input. So the given buffer
// should be at least kBase10MaximalLength + 1 characters long.
private final val kBase10MaximalLength = 17
private final val infinity_symbol = "Infinity"
private final val nan_symbol = "NaN"
private final val exponent_character = 'e'
private def handleSpecialValues(double_inspect: GrisuDouble,
writer: Writer): Unit =
{
if(double_inspect.isInfinite) {
if(double_inspect.toDouble < 0) {
writer.write('-')
}
writer.write(infinity_symbol)
} else if(double_inspect.isNaN) {
writer.write(nan_symbol)
}
}
// intBoxes is (length, point)
private def doubleToShortestAscii(v: GrisuDouble, buffer: Array[Char], intBoxes: IntBoxes): Boolean = {
assert(!v.isSpecial)
assert(v.toDouble >= 0.0)
val value = v.toDouble
if (value == 0.0) {
buffer(0) = '0'
buffer(1) = 0.toChar
intBoxes.a = 1
intBoxes.b = 1
true
} else {
val result = Grisu3(v, buffer, intBoxes)
if (result) intBoxes.b += intBoxes.a
else intBoxes.b = 0
result
}
}
// The minimal and maximal target exponent define the range of w's binary
// exponent, where 'w' is the result of multiplying the input by a cached power
// of ten.
//
// A different range might be chosen on a different platform, to optimize digit
// generation, but a smaller range requires more powers of ten to be cached.
private final val kMinimalTargetExponent = -60
private final val kMaximalTargetExponent = -32
// Provides a decimal representation of v.
// Returns true if it succeeds, otherwise the result cannot be trusted.
// There will be *length digits inside the buffer (not null-terminated).
// If the function returns true then
// v == (double) (buffer * 10^decimal_exponent).
// The digits in the buffer are the shortest representation possible: no
// 0.09999999999999999 instead of 0.1. The shorter representation will even be
// chosen even if the longer one would be closer to v.
// The last digit will be closest to the actual v. That is, even if several
// digits might correctly yield 'v' when read again, the closest will be
// computed.
private def Grisu3(v: GrisuDouble,
buffer: Array[Char],
intBoxes: IntBoxes): Boolean =
{
val w = v.asNormalizedDiyFp
// boundary_minus and boundary_plus are the boundaries between v and its
// closest floating-point neighbors. Any number strictly between
// boundary_minus and boundary_plus will round to v when convert to a double.
// Grisu3 will never output representations that lie exactly on a boundary.
val (boundary_minus, boundary_plus) = v.normalizedBoundaries
assert(boundary_plus.e == w.e)
val ten_mk_minimal_binary_exponent =
kMinimalTargetExponent - (w.e + DiyFp.kSignificandSize)
val ten_mk_maximal_binary_exponent =
kMaximalTargetExponent - (w.e + DiyFp.kSignificandSize)
val PowersOfTenCache.ResultBox(ten_mk, mk) = PowersOfTenCache.cachedPowerForBinaryExponentRange(
ten_mk_minimal_binary_exponent,
ten_mk_maximal_binary_exponent)
assert((kMinimalTargetExponent <= w.e + ten_mk.e +
DiyFp.kSignificandSize) &&
(kMaximalTargetExponent >= w.e + ten_mk.e +
DiyFp.kSignificandSize))
// Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a
// 64 bit significand and ten_mk is thus only precise up to 64 bits.
// The DiyFp.Times procedure rounds its result, and ten_mk is approximated
// too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now
// off by a small amount.
// In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w.
// In other words: let f = scaled_w.f() and e = scaled_w.e(), then
// (f-1) * 2^e < w*10^k < (f+1) * 2^e
w *= ten_mk
assert(w.e == boundary_plus.e + ten_mk.e + DiyFp.kSignificandSize)
// In theory it would be possible to avoid some recomputations by computing
// the difference between w and boundary_minus/plus (a power of 2) and to
// compute scaled_boundary_minus/plus by subtracting/adding from
// scaled_w. However the code becomes much less readable and the speed
// enhancements are not terriffic.
boundary_minus *= ten_mk
boundary_plus *= ten_mk
// DigitGen will generate the digits of scaled_w. Therefore we have
// v == (double) (scaled_w * 10^-mk).
// Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an
// integer than it will be updated. For instance if scaled_w == 1.23 then
// the buffer will be filled with "123" und the decimal_exponent will be
// decreased by 2.
val result = digitGen(boundary_minus, w, boundary_plus,
buffer, intBoxes)
intBoxes.b = -mk + intBoxes.b
result
}
// Generates the digits of input number w.
// w is a floating-point number (DiyFp), consisting of a significand and an
// exponent. Its exponent is bounded by kMinimalTargetExponent and
// kMaximalTargetExponent.
// Hence -60 <= w.e() <= -32.
//
// Returns false if it fails, in which case the generated digits in the buffer
// should not be used.
// Preconditions:
// * low, w and high are correct up to 1 ulp (unit in the last place). That
// is, their error must be less than a unit of their last digits.
// * low.e() == w.e() == high.e()
// * low < w < high, and taking into account their error: low~ <= high~
// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent
// Postconditions: returns false if procedure fails.
// otherwise:
// * buffer is not null-terminated, but len contains the number of digits.
// * buffer contains the shortest possible decimal digit-sequence
// such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the
// correct values of low and high (without their error).
// * if more than one decimal representation gives the minimal number of
// decimal digits then the one closest to W (where W is the correct value
// of w) is chosen.
// Remark: this procedure takes into account the imprecision of its input
// numbers. If the precision is not enough to guarantee all the postconditions
// then false is returned. This usually happens rarely (~0.5%).
//
// Say, for the sake of example, that
// w.e() == -48, and w.f() == 0x1234567890abcdef
// w's value can be computed by w.f() * 2^w.e()
// We can obtain w's integral digits by simply shifting w.f() by -w.e().
// -> w's integral part is 0x1234
// w's fractional part is therefore 0x567890abcdef.
// Printing w's integral part is easy (simply print 0x1234 in decimal).
// In order to print its fraction we repeatedly multiply the fraction by 10 and
// get each digit. Example the first digit after the point would be computed by
// (0x567890abcdef * 10) >> 48. -> 3
// The whole thing becomes slightly more complicated because we want to stop
// once we have enough digits. That is, once the digits inside the buffer
// represent 'w' we can stop. Everything inside the interval low - high
// represents w. However we have to pay attention to low, high and w's
// imprecision.
private def digitGen(low: DiyFp,
w: DiyFp,
high: DiyFp,
buffer: Array[Char],
intBoxes: IntBoxes): Boolean = // intBoxes is (length, kappa)
{
assert(low.e == w.e && w.e == high.e)
assert(low.f + ULong(1) <= high.f - ULong(1))
assert(kMinimalTargetExponent <= w.e && w.e <= kMaximalTargetExponent)
// low, w and high are imprecise, but by less than one ulp (unit in the last
// place).
// If we remove (resp. add) 1 ulp from low (resp. high) we are certain that
// the new numbers are outside of the interval we want the final
// representation to lie in.
// Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield
// numbers that are certain to lie in the interval. We will use this fact
// later on.
// We will now start by generating the digits within the uncertain
// interval. Later we will weed out representations that lie outside the safe
// interval and thus _might_ lie outside the correct interval.
var unit = ULong(1L)
val too_low = new DiyFp(low.f - unit, low.e)
val too_high = new DiyFp(high.f + unit, high.e)
// too_low and too_high are guaranteed to lie outside the interval we want the
// generated number in.
val unsafe_interval = too_high - too_low
// We now cut the input number into two parts: the integral digits and the
// fractionals. We will not write any decimal separator though, but adapt
// kappa instead.
// Reminder: we are currently computing the digits (stored inside the buffer)
// such that: too_low < buffer * 10^kappa < too_high
// We use too_high for the digit_generation and stop as soon as possible.
// If we stop early we effectively round down.
val one = new DiyFp(ULong(1L) << -w.e, w.e)
// Division by one is a shift.
var integrals = UInt((too_high.f >> -one.e).toInt)
// Modulo by one is an and.
var fractionals = too_high.f & (one.f - ULong(1))
biggestPowerTen(integrals, DiyFp.kSignificandSize - (-one.e), intBoxes)
var divisor = UInt(intBoxes.a)
var kappa = intBoxes.b
var length = 0
// Loop invariant: buffer = too_high / 10^kappa (integer division)
// The invariant holds for the first iteration: kappa has been initialized
// with the divisor exponent + 1. And the divisor is the biggest power of ten
// that is smaller than integrals.
var unsafeIntervalF = unsafe_interval.f
while(kappa > 0) {
val digit = (integrals / divisor).toInt
buffer(length) = ('0' + digit).toChar
length += 1
integrals %= divisor
kappa -= 1
// Note that kappa now equals the exponent of the divisor and that the
// invariant thus holds again.
val rest = (ULong(integrals.toLong) << -one.e) + fractionals
// Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e())
// Reminder: unsafe_interval.e() == one.e()
if(rest < unsafeIntervalF) {
// Rounding down (by not emitting the remaining digits) yields a number
// that lies within the unsafe interval.
too_high -= w
intBoxes.a = length
intBoxes.b = kappa
return roundWeed(buffer, length, too_high.f,
unsafeIntervalF, rest,
ULong(divisor.toLong) << -one.e, unit)
}
divisor /= UInt(10)
}
// The integrals have been generated. We are at the point of the decimal
// separator. In the following loop we simply multiply the remaining digits by
// 10 and divide by one. We just need to pay attention to multiply associated
// data (like the interval or 'unit'), too.
// Note that the multiplication by 10 does not overflow, because w.e >= -60
// and thus one.e >= -60.
assert(one.e >= -60)
assert(fractionals < one.f)
assert(ULong(-1L) / ULong(10) >= one.f)
do {
fractionals *= ULong(10L)
unit *= ULong(10L)
unsafe_interval.f *= ULong(10)
// Integer division by one.
val digit = (fractionals >> -one.e).toInt
buffer(length) = ('0' + digit).toChar
length += 1
fractionals &= one.f - ULong(1) // Modulo by one.
kappa -= 1
} while(fractionals >= unsafe_interval.f)
too_high -= w
intBoxes.a = length
intBoxes.b = kappa
roundWeed(buffer, length, too_high.f * unit,
unsafe_interval.f, fractionals, one.f, unit)
}
// Returns the biggest power of ten that is less than or equal to the given
// number. We furthermore receive the maximum number of bits 'number' has.
//
// Returns power == 10^(exponent_plus_one-1) such that
// power <= number < power * 10.
// If number_bits == 0 then 0^(0-1) is returned.
// The number of bits must be <= 32.
// Precondition: number < (1 << (number_bits + 1)).
// Inspired by the method for finding an integer log base 10 from here:
// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
private final val kSmallPowersOfTenS = Array[Int](
0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
)
private def kSmallPowersOfTen(i: Int) = UInt(kSmallPowersOfTenS(i))
private def biggestPowerTen(number: UInt,
number_bits: Int,
intBoxes: IntBoxes) // intBoxes is power.toInt, exponent_plus_one
{
assert(number < (UInt(1) << (number_bits + 1)))
// 1233/4096 is approximately 1/lg(10).
var exponent_plus_one_guess = ((number_bits + 1) * 1233 >> 12)
// We increment to skip over the first entry in the kPowersOf10 table.
// Note: kPowersOf10[i] == 10^(i-1).
exponent_plus_one_guess += 1
// We don't have any guarantees that 2^number_bits <= number.
// TODO(floitsch): can we change the 'while' into an 'if'? We definitely see
// number < (2^number_bits - 1), but I haven't encountered
// number < (2^number_bits - 2) yet.
while (number < kSmallPowersOfTen(exponent_plus_one_guess)) {
exponent_plus_one_guess -= 1
}
intBoxes.a = kSmallPowersOfTenS(exponent_plus_one_guess)
intBoxes.b = exponent_plus_one_guess
}
// Adjusts the last digit of the generated number, and screens out generated
// solutions that may be inaccurate. A solution may be inaccurate if it is
// outside the safe interval, or if we cannot prove that it is closer to the
// input than a neighboring representation of the same length.
//
// Input: * buffer containing the digits of too_high / 10^kappa
// * the buffer's length
// * distance_too_high_w == (too_high - w).f() * unit
// * unsafe_interval == (too_high - too_low).f() * unit
// * rest = (too_high - buffer * 10^kappa).f() * unit
// * ten_kappa = 10^kappa * unit
// * unit = the common multiplier
// Output: returns true if the buffer is guaranteed to contain the closest
// representable number to the input.
// Modifies the generated digits in the buffer to approach (round towards) w.
private def roundWeed(buffer: Array[Char],
length: Int,
distance_too_high_w: ULong,
unsafe_interval: ULong,
rest_ : ULong,
ten_kappa: ULong,
unit: ULong): Boolean =
{
var rest = rest_
val small_distance = distance_too_high_w - unit
val big_distance = distance_too_high_w + unit
// Let w_low = too_high - big_distance, and
// w_high = too_high - small_distance.
// Note: w_low < w < w_high
//
// The real w (* unit) must lie somewhere inside the interval
// ]w_low; w_high[ (often written as "(w_low; w_high)")
// Basically the buffer currently contains a number in the unsafe interval
// ]too_low; too_high[ with too_low < w < too_high
//
// too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// ^v 1 unit ^ ^ ^ ^
// boundary_high --------------------- . . . .
// ^v 1 unit . . . .
// - - - - - - - - - - - - - - - - - - - + - - + - - - - - - . .
// . . ^ . .
// . big_distance . . .
// . . . . rest
// small_distance . . . .
// v . . . .
// w_high - - - - - - - - - - - - - - - - - - . . . .
// ^v 1 unit . . . .
// w ---------------------------------------- . . . .
// ^v 1 unit v . . .
// w_low - - - - - - - - - - - - - - - - - - - - - . . .
// . . v
// buffer --------------------------------------------------+-------+--------
// . .
// safe_interval .
// v .
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .
// ^v 1 unit .
// boundary_low ------------------------- unsafe_interval
// ^v 1 unit v
// too_low - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
//
// Note that the value of buffer could lie anywhere inside the range too_low
// to too_high.
//
// boundary_low, boundary_high and w are approximations of the real boundaries
// and v (the input number). They are guaranteed to be precise up to one unit.
// In fact the error is guaranteed to be strictly less than one unit.
//
// Anything that lies outside the unsafe interval is guaranteed not to round
// to v when read again.
// Anything that lies inside the safe interval is guaranteed to round to v
// when read again.
// If the number inside the buffer lies inside the unsafe interval but not
// inside the safe interval then we simply do not know and bail out (returning
// false).
//
// Similarly we have to take into account the imprecision of 'w' when finding
// the closest representation of 'w'. If we have two potential
// representations, and one is closer to both w_low and w_high, then we know
// it is closer to the actual value v.
//
// By generating the digits of too_high we got the largest (closest to
// too_high) buffer that is still in the unsafe interval. In the case where
// w_high < buffer < too_high we try to decrement the buffer.
// This way the buffer approaches (rounds towards) w.
// There are 3 conditions that stop the decrementation process:
// 1) the buffer is already below w_high
// 2) decrementing the buffer would make it leave the unsafe interval
// 3) decrementing the buffer would yield a number below w_high and farther
// away than the current number. In other words:
// (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high
// Instead of using the buffer directly we use its distance to too_high.
// Conceptually rest ~= too_high - buffer
// We need to do the following tests in this order to avoid over- and
// underflows.
assert(rest <= unsafe_interval)
while (rest < small_distance && // Negated condition 1
unsafe_interval - rest >= ten_kappa && // Negated condition 2
(rest + ten_kappa < small_distance || // buffer{-1} > w_high
small_distance - rest >= rest + ten_kappa - small_distance))
{
buffer(length - 1) = (buffer(length - 1) - 1).toChar
rest += ten_kappa
}
// We have approached w+ as much as possible. We now test if approaching w-
// would require changing the buffer. If yes, then we have two possible
// representations close to w, but we cannot decide which one is closer.
if (rest < big_distance &&
unsafe_interval - rest >= ten_kappa &&
(rest + ten_kappa < big_distance ||
big_distance - rest > rest + ten_kappa - big_distance))
{
return false
}
// Weeding test.
// The safe interval is [too_low + 2 ulp; too_high - 2 ulp]
// Since too_low = too_high - unsafe_interval this is equivalent to
// [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp]
// Conceptually we have: rest ~= too_high - buffer
return (ULong(2) * unit <= rest) && (rest <= unsafe_interval - ULong(4) * unit)
}
private def createDecimalRepresentation(decimal_digits: Array[Char],
length: Int,
decimal_point: Int,
digits_after_point: Int,
writer: Writer): Unit =
{
// Create a representation that is padded with zeros if needed.
if (decimal_point <= 0) {
// "0.00000decimal_rep".
writer.write('0')
if (digits_after_point > 0) {
writer.write('.')
writer.write("0" * (-decimal_point))
assert(length <= digits_after_point - (-decimal_point))
writer.write(decimal_digits, 0, length)
val remaining_digits = digits_after_point - (-decimal_point) - length
writer.write("0" * remaining_digits)
}
} else if (decimal_point >= length) {
// "decimal_rep0000.00000" or "decimal_rep.0000"
writer.write(decimal_digits, 0, length)
writer.write("0" * (decimal_point - length))
if (digits_after_point > 0) {
writer.write('.')
writer.write("0" * digits_after_point)
}
} else {
// "decima.l_rep000"
assert(digits_after_point > 0)
writer.write(decimal_digits, 0, decimal_point)
writer.write('.')
assert(length - decimal_point <= digits_after_point)
writer.write(decimal_digits, decimal_point,
length - decimal_point)
val remaining_digits = digits_after_point - (length - decimal_point)
writer.write("0" * remaining_digits)
}
}
private def createExponentialRepresentation(decimal_digits: Array[Char],
length: Int,
exponent_ : Int,
writer: Writer): Unit =
{
var exponent = exponent_
assert(length != 0)
writer.write(decimal_digits(0))
if (length != 1)
{
writer.write('.')
writer.write(decimal_digits, 1, length - 1)
}
writer.write(exponent_character)
if (exponent < 0) {
writer.write('-')
exponent = -exponent
} else if (exponent == 0) {
writer.write('0')
return
}
assert(exponent < 10000)
if (exponent >= 100) {
writer.write(('0' + exponent / 100).toChar)
exponent %= 100
writer.write(('0' + exponent / 10).toChar)
exponent %= 10
writer.write(('0' + exponent).toChar)
} else if (exponent >= 10) {
writer.write(('0' + exponent / 10).toChar)
exponent %= 10
writer.write(('0' + exponent).toChar)
} else {
writer.write(('0' + exponent).toChar)
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy