@@ -484,6 +484,110 @@ end
484484hash (x:: Float32 , h:: UInt ) = hash (Float64 (x), h)
485485hash (x:: Float16 , h:: UInt ) = hash (Float64 (x), h)
486486
487+ # # generic hashing for rational values ##
488+
489+ function hash (x:: Real , h:: UInt )
490+ # decompose x as num*2^pow/den
491+ num, pow, den = decompose (x)
492+
493+ # handle special values
494+ num == 0 && den == 0 && return hash (NaN , h)
495+ num == 0 && return hash (ifelse (den > 0 , 0.0 , - 0.0 ), h)
496+ den == 0 && return hash (ifelse (num > 0 , Inf , - Inf ), h)
497+
498+ # normalize decomposition
499+ if den < 0
500+ num = - num
501+ den = - den
502+ end
503+ z = trailing_zeros (num)
504+ if z != 0
505+ num >>= z
506+ pow += z
507+ end
508+ z = trailing_zeros (den)
509+ if z != 0
510+ den >>= z
511+ pow -= z
512+ end
513+
514+ # handle values representable as Int64, UInt64, Float64
515+ if den == 1
516+ left = ndigits0z (num,2 ) + pow
517+ right = trailing_zeros (num) + pow
518+ if - 1074 <= right
519+ if 0 <= right && left <= 64
520+ left <= 63 && return hash (Int64 (num) << Int (pow), h)
521+ signbit (num) == signbit (den) && return hash (UInt64 (num) << Int (pow), h)
522+ end # typemin(Int64) handled by Float64 case
523+ left <= 1024 && left - right <= 53 && return hash (ldexp (Float64 (num),pow), h)
524+ end
525+ end
526+
527+ # handle generic rational values
528+ h = hash_integer (den, h)
529+ h = hash_integer (pow, h)
530+ h = hash_integer (num, h)
531+ return h
532+ end
533+
534+ #=
535+ `decompose(x)`: non-canonical decomposition of rational values as `num*2^pow/den`.
536+
537+ The decompose function is the point where rational-valued numeric types that support
538+ hashing hook into the hashing protocol. `decompose(x)` should return three integer
539+ values `num, pow, den`, such that the value of `x` is mathematically equal to
540+
541+ num*2^pow/den
542+
543+ The decomposition need not be canonical in the sense that it just needs to be *some*
544+ way to express `x` in this form, not any particular way – with the restriction that
545+ `num` and `den` may not share any odd common factors. They may, however, have powers
546+ of two in common – the generic hashing code will normalize those as necessary.
547+
548+ Special values:
549+
550+ - `x` is zero: `num` should be zero and `den` should have the same sign as `x`
551+ - `x` is infinite: `den` should be zero and `num` should have the same sign as `x`
552+ - `x` is not a number: `num` and `den` should both be zero
553+ =#
554+
555+ decompose (x:: Integer ) = x, 0 , 1
556+
557+ function decompose (x:: Float16 ):: NTuple{3,Int}
558+ isnan (x) && return 0 , 0 , 0
559+ isinf (x) && return ifelse (x < 0 , - 1 , 1 ), 0 , 0
560+ n = reinterpret (UInt16, x)
561+ s = (n & 0x03ff ) % Int16
562+ e = ((n & 0x7c00 ) >> 10 ) % Int
563+ s |= Int16 (e != 0 ) << 10
564+ d = ifelse (signbit (x), - 1 , 1 )
565+ s, e - 25 + (e == 0 ), d
566+ end
567+
568+ function decompose (x:: Float32 ):: NTuple{3,Int}
569+ isnan (x) && return 0 , 0 , 0
570+ isinf (x) && return ifelse (x < 0 , - 1 , 1 ), 0 , 0
571+ n = reinterpret (UInt32, x)
572+ s = (n & 0x007fffff ) % Int32
573+ e = ((n & 0x7f800000 ) >> 23 ) % Int
574+ s |= Int32 (e != 0 ) << 23
575+ d = ifelse (signbit (x), - 1 , 1 )
576+ s, e - 150 + (e == 0 ), d
577+ end
578+
579+ function decompose (x:: Float64 ):: Tuple{Int64, Int, Int}
580+ isnan (x) && return 0 , 0 , 0
581+ isinf (x) && return ifelse (x < 0 , - 1 , 1 ), 0 , 0
582+ n = reinterpret (UInt64, x)
583+ s = (n & 0x000fffffffffffff ) % Int64
584+ e = ((n & 0x7ff0000000000000 ) >> 52 ) % Int
585+ s |= Int64 (e != 0 ) << 52
586+ d = ifelse (signbit (x), - 1 , 1 )
587+ s, e - 1075 + (e == 0 ), d
588+ end
589+
590+
487591"""
488592 precision(num::AbstractFloat)
489593
0 commit comments