# 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 # 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. require "./ieee" # This "Do It Yourself Floating Point" struct implements a floating-point number # 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. # # NOTE: `DiyFP` is not designed to contain special Floats (*NaN* and *Infinity*). @[Deprecated] struct Float::Printer::DiyFP SIGNIFICAND_SIZE = 64 # Also known as the significand. property frac : UInt64 # Exponent. property exp : Int32 def initialize(@frac, @exp) end def initialize(@frac, exp : Int16) @exp = exp.to_i32 end def new(frac : Int32, exp) new frac.to_u64, exp end # Returns a new `DiyFP` calculated as `self - other`. # # The exponents of both numbers must be the same and the `frac` of `self` # must be greater than the *other*. # # NOTE: This result is not normalized. def -(other : DiyFP) : self self.class.new(frac - other.frac, exp) end MASK32 = 0xFFFFFFFF_u32 # Returns a new `DiyFP` calculated 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. # # NOTE: This result is not normalized. def *(other : DiyFP) : self a = frac >> 32 b = frac & MASK32 c = other.frac >> 32 d = other.frac & MASK32 ac = a*c bc = b*c ad = a*d bd = b*d tmp = (bd >> 32) + (ad & MASK32) + (bc & MASK32) # By adding 1U << 31 to tmp we round the final result. # Halfway cases will be round up. tmp += 1_u32 << 31 f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32) e = exp + other.exp + 64 self.class.new(f, e) end def normalize : DiyFP f = frac e = exp # This method is mainly called for normalizing boundaries. In general # boundaries need to be shifted by 10 bits. We thus optimize for this case. k10MSBits = 0xFFC0000000000000_u64 kUint64MSB = 0x8000000000000000_u64 while (f & k10MSBits) == 0 # puts " sig: #{f}" # puts " exp: #{e}" f <<= 10_u64 e -= 10 end while (f & kUint64MSB) == 0 # puts " sig: #{f}" # puts " exp: #{e}" f <<= 1_u64 e -= 1 end DiyFP.new(f, e) end def self.from_f(d : Float64 | Float32) : self frac, exp = IEEE.frac_and_exp(d) new(frac, exp) end # Normalize such that the most significant bit of `frac` is set. def self.from_f_normalized(v : Float64 | Float32) : self pre_normalized = from_f(v) f = pre_normalized.frac e = pre_normalized.exp # could be a denormal while (f & IEEE::HIDDEN_BIT_64) == 0 f <<= 1 e -= 1 end # do the final shifts in one go f <<= DiyFP::SIGNIFICAND_SIZE - IEEE::SIGNIFICAND_SIZE_64 e -= DiyFP::SIGNIFICAND_SIZE - IEEE::SIGNIFICAND_SIZE_64 DiyFP.new(f, e) end end