| // Copyright 2014 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Some Darwin/ARM libc versions fail to provide a standard compliant version |
| // of frexp and ldexp that could handle denormal floating point numbers. |
| // The frexp and ldexp implementations are translated from their Go version. |
| |
| #include <stdint.h> |
| |
| // Assume double and uint64_t are using the same endian. |
| union dint64 { |
| double d; |
| uint64_t u; |
| }; |
| |
| static const uint64_t mask = 0x7FF, bias = 1023; |
| static const int shift = 64 - 11 - 1; |
| static const uint64_t uvnan = 0x7FF8000000000001ULL, uvinf = 0x7FF0000000000000ULL, |
| uvneginf = 0xFFF0000000000000ULL; |
| static const double smallestnormal = 2.2250738585072014e-308; // 2**-1022 |
| |
| static inline uint64_t float64bits(double x) { |
| union dint64 u; |
| u.d = x; |
| return u.u; |
| } |
| static inline double float64frombits(uint64_t x) { |
| union dint64 u; |
| u.u = x; |
| return u.d; |
| } |
| static inline int isinf(double x) { |
| return float64bits(x) == uvinf || float64bits(x) == uvneginf; |
| } |
| static inline int isnan(double x) { |
| return x != x; |
| } |
| extern double fabs(double); |
| static double normalize(double x, int *exp) { |
| if (fabs(x) < smallestnormal) { |
| *exp = -52; |
| return x * (double)(1LL<<52); |
| } |
| *exp = 0; |
| return x; |
| } |
| |
| double ldexp(double frac, int exp) { |
| // special cases |
| if (frac == 0.0) return frac; |
| if (isinf(frac) || isnan(frac)) return frac; |
| |
| int e; |
| frac = normalize(frac, &e); |
| exp += e; |
| uint64_t x = float64bits(frac); |
| exp += (int)((x>>shift)&mask) - bias; |
| if (exp < -1074) { // underflow |
| if (frac < 0.0) return float64frombits(1ULL<<63); // -0.0 |
| return 0.0; |
| } |
| if (exp > 1023) { // overflow |
| if (frac < 0.0) return float64frombits(uvneginf); |
| return float64frombits(uvinf); |
| } |
| double m = 1; |
| if (exp < -1022) { // denormal |
| exp += 52; |
| m = 1.0 / (double)(1ULL<<52); |
| } |
| x &= ~(mask << shift); |
| x |= (uint64_t)(exp+bias) << shift; |
| return m * float64frombits(x); |
| } |
| |
| double frexp(double f, int *exp) { |
| *exp = 0; |
| // special cases |
| if (f == 0.0) return f; |
| if (isinf(f) || isnan(f)) return f; |
| |
| f = normalize(f, exp); |
| uint64_t x = float64bits(f); |
| *exp += (int)((x>>shift)&mask) - bias + 1; |
| x &= ~(mask << shift); |
| x |= (-1 + bias) << shift; |
| return float64frombits(x); |
| } |
| |
| // On Darwin/ARM, the kernel insists on running VFP in runfast mode, and it |
| // cannot deal with denormal floating point numbers in that mode, so we have |
| // to disable the runfast mode if the client uses ldexp/frexp (i.e. 5g). |
| void disable_vfp_runfast(void) __attribute__((constructor)); |
| void disable_vfp_runfast(void) { |
| __asm__ volatile ( |
| "fmrx r0, fpscr\n" |
| "bic r0, r0, $0x03000000\n" |
| "fmxr fpscr, r0\n" |
| : : : "r0" |
| ); |
| } |