ftz 0.0.1
Fast, reproducible floating-point arithmetic
Loading...
Searching...
No Matches
words.h
Go to the documentation of this file.
1#pragma once
2#include "ftz/config.h"
3#include "native/attributes.h"
4
5// HLSL consumers must enable and admit shaderInt64 explicitly. The WebGPU
6// compatible path remains 32-bit; native C++ uses uint64_t unconditionally.
7#ifndef FTZ_SHADER_INT64
8#define FTZ_SHADER_INT64 0
9#endif
10#if FTZ_SHADER_INT64 != 0 && FTZ_SHADER_INT64 != 1
11#error "FTZ_SHADER_INT64 must be 0 or 1"
12#endif
13#if !defined(__cplusplus) && !FTZ_SHADER_INT64
14#include "ftz/webgpu_words.h"
15#else
16#ifdef __cplusplus
17#include <bit>
18#include <cstdint>
19#endif
20
21namespace ftz { namespace detail { namespace math {
22#ifdef __cplusplus
23 using std::uint64_t;
24#endif
25 // The large-angle reducer consumes 32-bit limbs at this boundary.
26 struct unsigned_word_product { unsigned int low, high; };
27 native_constexpr inline unsigned_word_product word_pair(unsigned int low, unsigned int high) {
28 unsigned_word_product r; r.low = low; r.high = high; return r;
29 }
30 native_constexpr inline unsigned_word_product word_pair(uint64_t value) {
31 return word_pair((unsigned int)value, (unsigned int)(value >> 32));
32 }
33 native_constexpr inline uint64_t word_value(unsigned_word_product value) {
34 return (uint64_t(value.high) << 32) | uint64_t(value.low);
35 }
36 native_constexpr inline unsigned_word_product unsigned_multiply_words(unsigned int a, unsigned int b) {
37 return word_pair(uint64_t(a) * uint64_t(b));
38 }
39 native_constexpr inline unsigned_word_product word_pair_left(unsigned_word_product value, unsigned int shift) {
40 return word_pair(shift < 64u ? word_value(value) << shift : uint64_t(0));
41 }
42 native_constexpr inline unsigned int word_leading_zeros(unsigned int value) {
43#ifdef __cplusplus
44 return (unsigned int)std::countl_zero(value);
45#else
46 return value == 0u ? 32u : 31u - (unsigned int)firstbithigh(value);
47#endif
48 }
49 native_constexpr inline unsigned int word_top(uint64_t value) {
50#ifdef __cplusplus
51 return 63u - (unsigned int)std::countl_zero(value);
52#else
53 // DXC SPIR-V supports firstbithigh only for 32-bit components.
54 unsigned int high = (unsigned int)(value >> 32);
55 return high != 0u ? 63u - word_leading_zeros(high)
56 : 31u - word_leading_zeros((unsigned int)value);
57#endif
58 }
59 // Discarded bits contribute one sticky bit. No shift reaches 64.
60 native_constexpr inline uint64_t word_right_jam(uint64_t value, unsigned int shift) {
61 if (shift == 0u) return value;
62 if (shift >= 64u) return uint64_t(value != 0);
63 return (value >> shift) | uint64_t((value << (64u - shift)) != 0);
64 }
65 struct fp32_result { unsigned int bits, valid; };
66 native_constexpr inline fp32_result fp32_result_of(unsigned int bits, unsigned int valid) {
67 fp32_result r; r.bits = bits; r.valid = valid; return r;
68 }
69 native_constexpr inline unsigned int fp32_flush_word(unsigned int bits) {
70 return (bits & 0x7fffffffu) < 0x00800000u ? bits & 0x80000000u : bits;
71 }
72 native_constexpr inline bool fp32_finite(unsigned int bits) {
73 return (bits & 0x7f800000u) != 0x7f800000u;
74 }
75 // magnitude * 2^(exponent-61), rounded to nearest-even then signed FTZ.
76 // Alignment jams only when exponent differences exclude deep cancellation.
77 // The final rounding shift discards the jam bit but preserves its meaning.
78 native_constexpr inline fp32_result fp32_pack(uint64_t magnitude, int exponent, unsigned int sign) {
79 if (magnitude == 0) return fp32_result_of(sign, 1u);
80 int top = (int)word_top(magnitude);
81 int biased = exponent - 61 + top + 127;
82 if (biased >= 255) return fp32_result_of(0u, 0u);
83 int shift = top - 23;
84 int denormal_shift = -88 - exponent;
85 if (shift < denormal_shift) shift = denormal_shift;
86 unsigned int quotient;
87 if (shift <= 0) {
88 quotient = (unsigned int)(magnitude << (unsigned int)(-shift));
89 } else if (shift == 1) {
90 quotient = (unsigned int)(magnitude >> 1);
91 quotient += ((unsigned int)magnitude & quotient & 1u);
92 } else {
93 unsigned int window = (unsigned int)word_right_jam(magnitude, (unsigned int)(shift - 2));
94 quotient = window >> 2;
95 quotient += ((window & 2u) != 0u && ((window & 1u) != 0u || (quotient & 1u) != 0u)) ? 1u : 0u;
96 }
97 if (biased <= 0)
98 return fp32_result_of(sign | (quotient >= 0x00800000u ? 0x00800000u : 0u), 1u);
99 if (quotient == 0x01000000u) { quotient >>= 1; ++biased; }
100 if (biased >= 255) return fp32_result_of(0u, 0u);
101 return fp32_result_of(sign | ((unsigned int)biased << 23) | (quotient & 0x007fffffu), 1u);
102 }
103 native_constexpr inline fp32_result fp32_pack(unsigned_word_product magnitude, int exponent, unsigned int sign) {
104 return fp32_pack(word_value(magnitude), exponent, sign);
105 }
106 native_constexpr inline fp32_result fp32_fma_words(unsigned int a, unsigned int b, unsigned int c) {
107 if (!fp32_finite(a) || !fp32_finite(b) || !fp32_finite(c))
108 return fp32_result_of(0u, 0u);
109 a = fp32_flush_word(a); b = fp32_flush_word(b); c = fp32_flush_word(c);
110 unsigned int sign = (a ^ b) & 0x80000000u;
111 unsigned int csign = c & 0x80000000u;
112 unsigned int ea = (a >> 23) & 255u, eb = (b >> 23) & 255u, ec = (c >> 23) & 255u;
113 if (ea == 0u || eb == 0u)
114 return fp32_result_of(ec == 0u ? sign & csign : c, 1u);
115 unsigned int ma = (a & 0x7fffffu) | 0x800000u, mb = (b & 0x7fffffu) | 0x800000u;
116 uint64_t product = uint64_t(ma) * uint64_t(mb);
117 unsigned int top = word_top(product);
118 int exponent = (int)ea + (int)eb - 300 + (int)top;
119 product <<= 61u - top;
120 if (ec == 0u) return fp32_pack(product, exponent, sign);
121 uint64_t addend = uint64_t((c & 0x7fffffu) | 0x800000u) << 38;
122 int ce = (int)ec - 127;
123 if (exponent < ce) {
124 product = word_right_jam(product, (unsigned int)(ce - exponent));
125 exponent = ce;
126 } else {
127 addend = word_right_jam(addend, (unsigned int)(exponent - ce));
128 }
129 uint64_t magnitude;
130 if (sign == csign) {
131 magnitude = product + addend;
132 } else if (product < addend) {
133 magnitude = addend - product; sign = csign;
134 } else {
135 magnitude = product - addend;
136 if (magnitude == 0) sign = 0u;
137 }
138 return fp32_pack(magnitude, exponent, sign);
139 }
140}}}
141#endif
142
Word-pair arithmetic and exact binary32 packing, multiplication, and FMA repair.