From 063787007bfc82ed651e19225ccd518799024517 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Fri, 22 Dec 2017 01:16:26 +0100 Subject: [PATCH] Improve readability of Float::Printer::* docs --- src/float/printer.cr | 4 +- src/float/printer/diy_fp.cr | 26 ++++---- src/float/printer/grisu3.cr | 117 +++++++++++++++++++++--------------- src/float/printer/ieee.cr | 32 +++++----- 4 files changed, 104 insertions(+), 75 deletions(-) diff --git a/src/float/printer.cr b/src/float/printer.cr index 01d3a91a5404..0504bc5b85d6 100644 --- a/src/float/printer.cr +++ b/src/float/printer.cr @@ -1,13 +1,13 @@ require "./printer/*" -# Float::Printer is based on Grisu3 algorithm described in the 2004 paper +# `Float::Printer` is based on Grisu3 algorithm described in the 2004 paper # "Printing Floating-Point Numbers Quickly and Accurately with Integers" by # Florian Loitsch. module Float::Printer extend self BUFFER_SIZE = 128 - # Converts Float *v* to a string representation and prints it onto *io* + # Converts `Float` *v* to a string representation and prints it onto *io*. # # It is used by `Float64#to_s` and it is probably not necessary to use # this directly. diff --git a/src/float/printer/diy_fp.cr b/src/float/printer/diy_fp.cr index 0547e79a700e..e7931ba63427 100644 --- a/src/float/printer/diy_fp.cr +++ b/src/float/printer/diy_fp.cr @@ -1,4 +1,5 @@ # DiyFP is ported from the C++ "double-conversions" library. +# # The following is their license: # Copyright 2010 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -30,15 +31,18 @@ require "./ieee" # This "Do It Yourself Floating Point" struct implements a floating-point number -# with a `UIht64` significand and an `Int32` exponent. Normalized DiyFP numbers will +# with a `UInt64` significand and an `Int32` exponent. Normalized `DiyFP` numbers will # have the most significant bit of the significand set. # Multiplication and Subtraction do not normalize their results. -# DiyFP is not designed to contain special Floats (NaN and Infinity). +# +# NOTE: `DiyFP` is not designed to contain special Floats (*NaN* and *Infinity*). struct Float::Printer::DiyFP SIGNIFICAND_SIZE = 64 - # Also known as the significand + + # Also known as the significand. property frac : UInt64 - # exponent + + # Exponent. property exp : Int32 def initialize(@frac, @exp) @@ -52,26 +56,26 @@ struct Float::Printer::DiyFP new frac.to_u64, exp end - # Returns a new `DiyFP` caculated as self - *other*. + # Returns a new `DiyFP` caculated as `self - other`. # - # The exponents of both numbers must be the same and the frac of self must be - # greater than the other. + # The exponents of both numbers must be the same and the `frac` of `self` + # must be greater than the *other*. # - # This result is not normalized. + # NOTE: This result is not normalized. def -(other : DiyFP) self.class.new(frac - other.frac, exp) end MASK32 = 0xFFFFFFFF_u32 - # Returns a new `DiyFP` caculated as self * *other*. + # Returns a new `DiyFP` caculated as `self * other`. # # Simply "emulates" a 128 bit multiplication. # However: the resulting number only contains 64 bits. The least # significant 64 bits are only used for rounding the most significant 64 # bits. # - # This result is not normalized. + # NOTE: This result is not normalized. def *(other : DiyFP) a = frac >> 32 b = frac & MASK32 @@ -119,7 +123,7 @@ struct Float::Printer::DiyFP new(frac, exp) end - # Normalize such that the most signficiant bit of frac is set + # Normalize such that the most signficiant bit of `frac` is set. def self.from_f_normalized(v : Float64 | Float32) pre_normalized = from_f(v) f = pre_normalized.frac diff --git a/src/float/printer/grisu3.cr b/src/float/printer/grisu3.cr index 17734ed5c204..6deec1481400 100644 --- a/src/float/printer/grisu3.cr +++ b/src/float/printer/grisu3.cr @@ -1,4 +1,5 @@ # Grisu3 is ported from the C++ "double-conversions" library. +# # The following is their license: # Copyright 2012 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -34,23 +35,28 @@ require "./cached_powers" module Float::Printer::Grisu3 extend self + # :nodoc: + # # 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 pointer containing the digits of too_high / 10^kappa - # * the buffer's length - # * distance_too_high_w == (too_high - w).frac * unit - # * unsafe_interval == (too_high - too_low).frac * unit - # * rest = (too_high - buffer * 10^kappa).frac * 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. - def round_weed(buffer_p, length, distance_too_high_w, unsafe_interval, rest, ten_kappa, unit) - buffer = buffer_p.to_slice(128) + # Input: + # * *buffer_ptr*: buffer pointer containing the digits of `too_high / 10^kappa` + # * *length*: the buffer's length + # * *distance_too_high_w*: `(too_high - w).frac * unit` + # * *unsafe_interval*: `(too_high - too_low).frac * unit` + # * *rest*: `(too_high - buffer * 10^kappa).frac * 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*. + def round_weed(buffer_ptr, length, distance_too_high_w, unsafe_interval, rest, ten_kappa, unit) + buffer = buffer_ptr.to_slice(128) small_distance = distance_too_high_w - unit big_distance = distance_too_high_w + unit @@ -154,43 +160,53 @@ module Float::Printer::Grisu3 return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit) end - # 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. + # :nodoc: # - # Returns false if it fails, in which case the generated digits in the buffer + # 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 + # * *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. + # + # NOTE: 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%). + # 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). + # Say, for the sake of example, that: + # w.e() == -48 && 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 + # (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 @@ -284,17 +300,22 @@ module Float::Printer::Grisu3 end end + # :nodoc: + # # Provides a decimal representation of *v*. # # Returns a `Tuple` of `{status, decimal_exponent, length}` - # *status* will be true if it succeeds, otherwise the result cannot be + # *status* will be `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 satatus as true true then - # v == (buffer * 10^decimal_exponent).to_f - # 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*. + # If the function returns status as `true` then + # v == (buffer * 10^decimal_exponent).to_f + # + # 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. diff --git a/src/float/printer/ieee.cr b/src/float/printer/ieee.cr index 5e9893f46f43..5583e654cdf5 100644 --- a/src/float/printer/ieee.cr +++ b/src/float/printer/ieee.cr @@ -1,4 +1,5 @@ # IEEE is ported from the C++ "double-conversions" library. +# # The following is their license: # Copyright 2012 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -30,20 +31,21 @@ module Float::Printer::IEEE extend self - EXPONENT_MASK_64 = 0x7FF0000000000000_u64 - SIGNIFICAND_MASK_64 = 0x000FFFFFFFFFFFFF_u64 - HIDDEN_BIT_64 = 0x0010000000000000_u64 - PHYSICAL_SIGNIFICAND_SIZE_64 = 52 # Excludes the hidden bit - SIGNIFICAND_SIZE_64 = 53 + EXPONENT_MASK_64 = 0x7FF0000000000000_u64 + SIGNIFICAND_MASK_64 = 0x000FFFFFFFFFFFFF_u64 + HIDDEN_BIT_64 = 0x0010000000000000_u64 + # Excludes the hidden bit + PHYSICAL_SIGNIFICAND_SIZE_64 = 52 + SIGNIFICAND_SIZE_64 = 53 EXPONENT_BIAS_64 = 0x3FF + PHYSICAL_SIGNIFICAND_SIZE_64 DENORMAL_EXPONENT_64 = -EXPONENT_BIAS_64 + 1 SIGN_MASK_64 = 0x8000000000000000_u64 - - EXPONENT_MASK_32 = 0x7F800000_u32 - SIGNIFICAND_MASK_32 = 0x007FFFFF_u32 - HIDDEN_BIT_32 = 0x00800000_u32 - PHYSICAL_SIGNIFICAND_SIZE_32 = 23 # Excludes the hidden bit - SIGNIFICAND_SIZE_32 = 24 + EXPONENT_MASK_32 = 0x7F800000_u32 + SIGNIFICAND_MASK_32 = 0x007FFFFF_u32 + HIDDEN_BIT_32 = 0x00800000_u32 + # Excludes the hidden bit + PHYSICAL_SIGNIFICAND_SIZE_32 = 23 + SIGNIFICAND_SIZE_32 = 24 EXPONENT_BIAS_32 = 0x7F + PHYSICAL_SIGNIFICAND_SIZE_32 DENORMAL_EXPONENT_32 = -EXPONENT_BIAS_32 + 1 SIGN_MASK_32 = 0x80000000_u32 @@ -89,9 +91,11 @@ module Float::Printer::IEEE end # Computes the two boundaries of *v*. - # The bigger boundary (m_plus) is normalized. The lower boundary has the same - # exponent as m_plus. - # Precondition: the value encoded by this Flaot must be greater than 0. + # + # The bigger boundary (*m_plus*) is normalized. The lower boundary has the same + # exponent as *m_plus*. + # + # Precondition: the value encoded by this `Float` must be greater than 0. def normalized_boundaries(v : Float64) w = DiyFP.from_f(v) m_plus = DiyFP.new((w.frac << 1) + 1, w.exp - 1).normalize