Skip to main content

font_types/
fixed.rs

1//! fixed-point numerical types
2
3use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
4
5// shared between Fixed, F26Dot6, F2Dot14, F4Dot12, F6Dot10
6macro_rules! fixed_impl {
7    ($name:ident, $bits:literal, $fract_bits:literal, $ty:ty) => {
8        #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
9        #[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern, bytemuck::NoUninit))]
10        #[repr(transparent)]
11        #[doc = concat!(stringify!($bits), "-bit signed fixed point number with ", stringify!($fract_bits), " bits of fraction." )]
12        pub struct $name($ty);
13        impl $name {
14            /// Minimum value.
15            pub const MIN: Self = Self(<$ty>::MIN);
16
17            /// Maximum value.
18            pub const MAX: Self = Self(<$ty>::MAX);
19
20            /// This type's smallest representable value
21            pub const EPSILON: Self = Self(1);
22
23            /// Representation of 0.0.
24            pub const ZERO: Self = Self(0);
25
26            /// Representation of 1.0.
27            pub const ONE: Self = Self(1 << $fract_bits);
28
29            /// Representation of -1.0.
30            pub const NEG_ONE: Self = Self((!0 << $fract_bits) as $ty);
31
32            const INT_MASK: $ty = !0 << $fract_bits;
33            const ROUND: $ty = 1 << ($fract_bits - 1);
34            const FRACT_BITS: usize = $fract_bits;
35
36            /// Creates a new fixed point value from the underlying bit representation.
37            #[inline(always)]
38            pub const fn from_bits(bits: $ty) -> Self {
39                Self(bits)
40            }
41
42            /// Returns the underlying bit representation of the value.
43            #[inline(always)]
44            pub const fn to_bits(self) -> $ty {
45                self.0
46            }
47
48            //TODO: is this actually useful?
49            /// Returns the nearest integer value.
50            #[inline(always)]
51            pub const fn round(self) -> Self {
52                Self(self.0.wrapping_add(Self::ROUND) & Self::INT_MASK)
53            }
54
55            /// Returns the absolute value of the number.
56            #[inline(always)]
57            pub const fn abs(self) -> Self {
58                Self(self.0.wrapping_abs())
59            }
60
61            /// Returns the largest integer less than or equal to the number.
62            #[inline(always)]
63            pub const fn floor(self) -> Self {
64                Self(self.0 & Self::INT_MASK)
65            }
66
67            /// Returns the fractional part of the number.
68            #[inline(always)]
69            pub const fn fract(self) -> Self {
70                Self(self.0 - self.floor().0)
71            }
72
73            /// Wrapping addition.
74            #[inline(always)]
75            pub fn wrapping_add(self, other: Self) -> Self {
76                Self(self.0.wrapping_add(other.0))
77            }
78
79            /// Saturating addition.
80            #[inline(always)]
81            pub const fn saturating_add(self, other: Self) -> Self {
82                Self(self.0.saturating_add(other.0))
83            }
84
85            /// Checked addition.
86            #[inline(always)]
87            pub fn checked_add(self, other: Self) -> Option<Self> {
88                self.0.checked_add(other.0).map(|inner| Self(inner))
89            }
90
91            /// Wrapping substitution.
92            #[inline(always)]
93            pub const fn wrapping_sub(self, other: Self) -> Self {
94                Self(self.0.wrapping_sub(other.0))
95            }
96
97            /// Saturating substitution.
98            #[inline(always)]
99            pub const fn saturating_sub(self, other: Self) -> Self {
100                Self(self.0.saturating_sub(other.0))
101            }
102
103            /// The representation of this number as a big-endian byte array.
104            #[inline(always)]
105            pub const fn to_be_bytes(self) -> [u8; $bits / 8] {
106                self.0.to_be_bytes()
107            }
108        }
109
110        impl Add for $name {
111            type Output = Self;
112            #[inline(always)]
113            fn add(self, other: Self) -> Self {
114                Self(self.0.wrapping_add(other.0))
115            }
116        }
117
118        impl AddAssign for $name {
119            #[inline(always)]
120            fn add_assign(&mut self, other: Self) {
121                *self = *self + other;
122            }
123        }
124
125        impl Sub for $name {
126            type Output = Self;
127            #[inline(always)]
128            fn sub(self, other: Self) -> Self {
129                Self(self.0.wrapping_sub(other.0))
130            }
131        }
132
133        impl SubAssign for $name {
134            #[inline(always)]
135            fn sub_assign(&mut self, other: Self) {
136                *self = *self - other;
137            }
138        }
139
140        impl Neg for $name {
141            type Output = Self;
142            #[inline(always)]
143            fn neg(self) -> Self {
144                Self(self.0.wrapping_neg())
145            }
146        }
147    };
148}
149
150impl Fixed {
151    /// Multiplies `self` by `a` and divides the product by `b`.
152    // This one is specifically not always inlined due to size and
153    // frequency of use. We leave it to compiler discretion.
154    #[inline]
155    pub const fn mul_div(&self, a: Self, b: Self) -> Self {
156        let mut sign = 1;
157        let mut su = self.0 as u64;
158        let mut au = a.0 as u64;
159        let mut bu = b.0 as u64;
160        if self.0 < 0 {
161            su = 0u64.wrapping_sub(su);
162            sign = -1;
163        }
164        if a.0 < 0 {
165            au = 0u64.wrapping_sub(au);
166            sign = -sign;
167        }
168        if b.0 < 0 {
169            bu = 0u64.wrapping_sub(bu);
170            sign = -sign;
171        }
172        let result = if bu > 0 {
173            su.wrapping_mul(au).wrapping_add(bu >> 1) / bu
174        } else {
175            0x7FFFFFFF
176        };
177        Self(if sign < 0 {
178            (result as i32).wrapping_neg()
179        } else {
180            result as i32
181        })
182    }
183}
184
185impl Mul for Fixed {
186    type Output = Self;
187
188    #[inline(always)]
189    fn mul(self, other: Self) -> Self::Output {
190        let ab = self.0 as i64 * other.0 as i64;
191        Self(((ab + 0x8000 - i64::from(ab < 0)) >> 16) as i32)
192    }
193}
194
195impl Div for Fixed {
196    type Output = Self;
197
198    fn div(self, other: Self) -> Self {
199        let sign = (self.0 < 0) ^ (other.0 < 0);
200        let au = self.0.unsigned_abs() as u64;
201        let bu = other.0.unsigned_abs() as u64;
202        let q = if bu == 0 {
203            0x7FFFFFFF_u32
204        } else {
205            (((au << 16) + (bu >> 1)) / bu) as u32
206        };
207        Self(if sign {
208            (q as i32).wrapping_neg()
209        } else {
210            q as i32
211        })
212    }
213}
214
215impl Mul for F26Dot6 {
216    type Output = Self;
217
218    #[inline(always)]
219    fn mul(self, other: Self) -> Self::Output {
220        let ab = self.0 as i64 * other.0 as i64;
221        Self(((ab + 32 - i64::from(ab < 0)) >> 6) as i32)
222    }
223}
224
225impl Div for F26Dot6 {
226    type Output = Self;
227
228    fn div(self, other: Self) -> Self {
229        let sign = (self.0 < 0) ^ (other.0 < 0);
230        let au = self.0.unsigned_abs() as u64;
231        let bu = other.0.unsigned_abs() as u64;
232        let q = if bu == 0 {
233            0x7FFFFFFF_u32
234        } else {
235            (((au << 6) + (bu >> 1)) / bu) as u32
236        };
237        Self(if sign {
238            (q as i32).wrapping_neg()
239        } else {
240            q as i32
241        })
242    }
243}
244
245/// Implements multiplication and division assignment operators for fixed
246/// types.
247macro_rules! fixed_mul_div_assign {
248    ($ty:ty) => {
249        impl MulAssign for $ty {
250            #[inline(always)]
251            fn mul_assign(&mut self, rhs: Self) {
252                *self = *self * rhs;
253            }
254        }
255
256        impl DivAssign for $ty {
257            #[inline(always)]
258            fn div_assign(&mut self, rhs: Self) {
259                *self = *self / rhs;
260            }
261        }
262    };
263}
264
265/// impl float conversion methods.
266///
267/// We convert to different float types in order to ensure we can roundtrip
268/// without floating point error.
269macro_rules! float_conv {
270    // default invocation: we will impl Display/Default/Serialize/Deserialize
271    ($name:ident, $to:ident, $from:ident, $ty:ty) => {
272        float_conv!($name, $to, $from, $ty, no_fmt);
273
274        //hack: we can losslessly go to float, so use those fmt impls
275        impl std::fmt::Display for $name {
276            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
277                self.$to().fmt(f)
278            }
279        }
280
281        impl std::fmt::Debug for $name {
282            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
283                self.$to().fmt(f)
284            }
285        }
286
287        #[cfg(feature = "serde")]
288        impl ::serde::Serialize for $name {
289            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
290            where
291                S: ::serde::Serializer,
292            {
293                <$ty>::serialize(&$name::$to(*self), serializer)
294            }
295        }
296
297        #[cfg(feature = "serde")]
298        impl<'de> ::serde::Deserialize<'de> for $name {
299            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
300            where
301                D: ::serde::Deserializer<'de>,
302            {
303                <$ty>::deserialize(deserializer).map($name::$from)
304            }
305        }
306    };
307    // explicitly opt out of Display/Default/Serialize/Deserialize
308    // (for types that get both f32 & f64)
309    ($name:ident, $to:ident, $from:ident, $ty:ty, no_fmt) => {
310        impl $name {
311            #[doc = concat!("Creates a fixed point value from a ", stringify!($ty), ".")]
312            ///
313            /// This operation is lossy; the float will be rounded to the nearest
314            /// representable value.
315            #[inline(always)]
316            pub fn $from(x: $ty) -> Self {
317                // When x is positive: 1.0 - 0.5 =  0.5
318                // When x is negative: 0.0 - 0.5 = -0.5
319                let frac = (x.is_sign_positive() as u8 as $ty) - 0.5;
320                Self((x * Self::ONE.0 as $ty + frac) as _)
321            }
322
323            #[doc = concat!("Returns the value as an ", stringify!($ty), ".")]
324            ///
325            /// This operation is lossless: all representable values can be
326            /// round-tripped.
327            #[inline(always)]
328            pub fn $to(self) -> $ty {
329                let int = ((self.0 & Self::INT_MASK) >> Self::FRACT_BITS) as $ty;
330                let fract = (self.0 & !Self::INT_MASK) as $ty / Self::ONE.0 as $ty;
331                int + fract
332            }
333        }
334    };
335}
336
337fixed_impl!(F2Dot14, 16, 14, i16);
338fixed_impl!(F4Dot12, 16, 12, i16);
339fixed_impl!(F6Dot10, 16, 10, i16);
340fixed_impl!(Fixed, 32, 16, i32);
341fixed_impl!(F26Dot6, 32, 6, i32);
342fixed_impl!(F48Dot16, 64, 16, i64);
343
344fixed_mul_div_assign!(Fixed);
345fixed_mul_div_assign!(F26Dot6);
346
347float_conv!(F2Dot14, to_f32, from_f32, f32);
348float_conv!(F4Dot12, to_f32, from_f32, f32);
349float_conv!(F6Dot10, to_f32, from_f32, f32);
350float_conv!(F48Dot16, to_f64, from_f64, f64);
351float_conv!(F2Dot14, to_f64, from_f64, f64, no_fmt);
352float_conv!(F4Dot12, to_f64, from_f64, f64, no_fmt);
353float_conv!(F6Dot10, to_f64, from_f64, f64, no_fmt);
354
355float_conv!(Fixed, to_f64, from_f64, f64);
356float_conv!(F26Dot6, to_f64, from_f64, f64);
357crate::newtype_scalar!(F2Dot14, [u8; 2]);
358crate::newtype_scalar!(F4Dot12, [u8; 2]);
359crate::newtype_scalar!(F6Dot10, [u8; 2]);
360crate::newtype_scalar!(Fixed, [u8; 4]);
361
362impl Fixed {
363    /// Creates a 16.16 fixed point value from a 32 bit integer.
364    #[inline(always)]
365    pub const fn from_i32(i: i32) -> Self {
366        Self(i << 16)
367    }
368
369    /// Converts a 16.16 fixed point value to a 32 bit integer, rounding off
370    /// the fractional bits.
371    #[inline(always)]
372    pub const fn to_i32(self) -> i32 {
373        self.0.wrapping_add(0x8000) >> 16
374    }
375
376    /// Converts a 16.16 to 26.6 fixed point value.
377    #[inline(always)]
378    pub const fn to_f26dot6(self) -> F26Dot6 {
379        F26Dot6(self.0.wrapping_add(0x200) >> 10)
380    }
381
382    /// Converts a 16.16 to 2.14 fixed point value.
383    ///
384    /// This specific conversion is defined by the spec:
385    /// <https://learn.microsoft.com/en-us/typography/opentype/spec/otvaroverview#coordinate-scales-and-normalization>
386    ///
387    /// "5. Convert the final, normalized 16.16 coordinate value to 2.14 by this method: add 0x00000002,
388    /// and sign-extend shift to the right by 2."
389    #[inline(always)]
390    pub const fn to_f2dot14(self) -> F2Dot14 {
391        F2Dot14((self.0.wrapping_add(2) >> 2) as _)
392    }
393
394    /// Converts a 16.16 fixed point value to a single precision floating
395    /// point value.
396    ///
397    /// This operation is lossy. Use `to_f64()` for a lossless conversion.
398    #[inline(always)]
399    pub fn to_f32(self) -> f32 {
400        const SCALE_FACTOR: f32 = 1.0 / 65536.0;
401        self.0 as f32 * SCALE_FACTOR
402    }
403
404    /// Converts a 16.16 to a 48.16 fixed point value.
405    ///
406    /// This conversion is exact.
407    #[inline(always)]
408    pub const fn to_f48dot16(self) -> F48Dot16 {
409        F48Dot16(self.0 as i64)
410    }
411
412    /// Applies an item variation delta, returning the varied value as a
413    /// single precision floating point number.
414    ///
415    /// A delta for a 16.16 valued target is a raw integer count of the
416    /// target's own quantum, so the accumulated 48.16 delta is scaled by
417    /// 1/65536 on application. The result is intentionally not rounded
418    /// back to 16.16.
419    #[inline(always)]
420    pub fn apply_delta(self, delta: F48Dot16) -> f32 {
421        self.to_f32() + (delta.to_f64() / 65536.0) as f32
422    }
423
424    /// Multiplies by a 32 bit integer, producing the product as a 48.16
425    /// fixed point value.
426    ///
427    /// This is the shape of variation delta accumulation: a raw integer
428    /// delta scaled by a 16.16 scalar. The arithmetic wraps rather than
429    /// panicking, following this module's style -- though the widened
430    /// product of two 32 bit values is at most 2^62 and cannot actually
431    /// wrap.
432    #[inline(always)]
433    pub const fn mul_i32(self, value: i32) -> F48Dot16 {
434        F48Dot16((self.0 as i64).wrapping_mul(value as i64))
435    }
436}
437
438impl From<i32> for Fixed {
439    fn from(value: i32) -> Self {
440        Self::from_i32(value)
441    }
442}
443
444impl F26Dot6 {
445    /// Creates a 26.6 fixed point value from a 32 bit integer.
446    #[inline(always)]
447    pub const fn from_i32(i: i32) -> Self {
448        Self(i << 6)
449    }
450
451    /// Converts a 26.6 fixed point value to a 32 bit integer, rounding off
452    /// the fractional bits.
453    #[inline(always)]
454    pub const fn to_i32(self) -> i32 {
455        self.0.wrapping_add(32) >> 6
456    }
457
458    /// Converts a 26.6 fixed point value to a single precision floating
459    /// point value.
460    ///
461    /// This operation is lossy. Use `to_f64()` for a lossless conversion.
462    #[inline(always)]
463    pub fn to_f32(self) -> f32 {
464        const SCALE_FACTOR: f32 = 1.0 / 64.0;
465        self.0 as f32 * SCALE_FACTOR
466    }
467}
468
469impl F48Dot16 {
470    /// Creates a 48.16 fixed point value from a 64 bit integer.
471    #[inline(always)]
472    pub const fn from_i64(i: i64) -> Self {
473        Self(i << 16)
474    }
475
476    /// Creates a 48.16 fixed point value from a 32 bit integer.
477    #[inline(always)]
478    pub const fn from_i32(i: i32) -> Self {
479        Self((i as i64) << 16)
480    }
481
482    /// Converts a 48.16 fixed point value to a 64 bit integer, rounding off
483    /// the fractional bits.
484    #[inline(always)]
485    pub const fn to_i64(self) -> i64 {
486        self.0.wrapping_add(0x8000) >> 16
487    }
488
489    /// Converts a 48.16 fixed point value to a 32 bit integer, rounding off
490    /// the fractional bits.
491    ///
492    /// This truncates the integral bits if the value is too large to fit.
493    #[inline(always)]
494    pub const fn to_i32(self) -> i32 {
495        self.to_i64() as i32
496    }
497
498    /// Converts a 48.16 fixed point value to a single precision floating
499    /// point value.
500    ///
501    /// This operation is lossy.
502    #[inline(always)]
503    pub const fn to_f32(self) -> f32 {
504        const SCALE_FACTOR: f64 = 1.0 / 65536.0;
505        (self.0 as f64 * SCALE_FACTOR) as f32
506    }
507
508    /// Converts a 48.16 to a 16.16 fixed point value.
509    ///
510    /// The fractional bits carry over exactly; this truncates the integral
511    /// bits if the value is too large to fit.
512    #[inline(always)]
513    pub const fn to_fixed(self) -> Fixed {
514        Fixed(self.0 as i32)
515    }
516}
517
518impl F2Dot14 {
519    /// Applies an item variation delta, returning the varied value as a
520    /// single precision floating point number.
521    ///
522    /// A delta for a 2.14 valued target is a raw integer count of the
523    /// target's own quantum, so the accumulated 48.16 delta is scaled by
524    /// one quarter of one percent -- 1/16384 -- on application. The result
525    /// is intentionally not rounded back to 2.14.
526    #[inline(always)]
527    pub fn apply_delta(self, delta: F48Dot16) -> f32 {
528        self.to_f32() + (delta.to_f64() / 16384.0) as f32
529    }
530
531    /// Converts a 2.14 to 16.16 fixed point value.
532    #[inline(always)]
533    pub const fn to_fixed(self) -> Fixed {
534        Fixed(self.0 as i32 * 4)
535    }
536}
537
538#[cfg(test)]
539mod tests {
540    #![allow(overflowing_literals)] // we want to specify byte values directly
541    use super::*;
542
543    #[test]
544    fn f2dot14_floats() {
545        // Examples from https://docs.microsoft.com/en-us/typography/opentype/spec/otff#data-types
546        assert_eq!(F2Dot14(0x7fff), F2Dot14::from_f32(1.999939));
547        assert_eq!(F2Dot14(0x7000), F2Dot14::from_f32(1.75));
548        assert_eq!(F2Dot14(0x0001), F2Dot14::from_f32(0.0000610356));
549        assert_eq!(F2Dot14(0x0000), F2Dot14::from_f32(0.0));
550        assert_eq!(F2Dot14(0xffff), F2Dot14::from_f32(-0.000061));
551        assert_eq!(F2Dot14(0x8000), F2Dot14::from_f32(-2.0));
552    }
553
554    #[test]
555    fn roundtrip_f2dot14() {
556        for i in i16::MIN..=i16::MAX {
557            let val = F2Dot14(i);
558            assert_eq!(val, F2Dot14::from_f32(val.to_f32()));
559        }
560    }
561
562    #[test]
563    fn round_f2dot14() {
564        assert_eq!(F2Dot14(0x7000).round(), F2Dot14::from_f32(-2.0));
565        assert_eq!(F2Dot14(0x1F00).round(), F2Dot14::from_f32(0.0));
566        assert_eq!(F2Dot14(0x2000).round(), F2Dot14::from_f32(1.0));
567    }
568
569    #[test]
570    fn round_fixed() {
571        //TODO: make good test cases
572        assert_eq!(Fixed(0x0001_7FFE).round(), Fixed(0x0001_0000));
573        assert_eq!(Fixed(0x0001_7FFF).round(), Fixed(0x0001_0000));
574        assert_eq!(Fixed(0x0001_8000).round(), Fixed(0x0002_0000));
575    }
576
577    // disabled because it's slow; these were just for my edification anyway
578    //#[test]
579    //fn roundtrip_fixed() {
580    //for i in i32::MIN..=i32::MAX {
581    //let val = Fixed(i);
582    //assert_eq!(val, Fixed::from_f64(val.to_f64()));
583    //}
584    //}
585
586    #[test]
587    fn fixed_floats() {
588        assert_eq!(Fixed(0x7fff_0000), Fixed::from_f64(32767.));
589        assert_eq!(Fixed(0x7000_0001), Fixed::from_f64(28672.00001525879));
590        assert_eq!(Fixed(0x0001_0000), Fixed::from_f64(1.0));
591        assert_eq!(Fixed(0x0000_0000), Fixed::from_f64(0.0));
592        assert_eq!(
593            Fixed(i32::from_be_bytes([0xff; 4])),
594            Fixed::from_f64(-0.000015259)
595        );
596        assert_eq!(Fixed(0x7fff_ffff), Fixed::from_f64(32768.0));
597    }
598
599    // We lost the f64::round() intrinsic when dropping std and the
600    // alternative implementation was very slightly incorrect, throwing
601    // off some tests. This makes sure we match.
602    #[test]
603    fn fixed_floats_rounding() {
604        fn with_round_intrinsic(x: f64) -> Fixed {
605            Fixed((x * 65536.0).round() as i32)
606        }
607        // These particular values were tripping up tests
608        let inputs = [0.05, 0.6, 0.2, 0.4, 0.67755];
609        for input in inputs {
610            assert_eq!(Fixed::from_f64(input), with_round_intrinsic(input));
611            // Test negated values as well for good measure
612            assert_eq!(Fixed::from_f64(-input), with_round_intrinsic(-input));
613        }
614    }
615
616    #[test]
617    fn fixed_to_int() {
618        assert_eq!(Fixed::from_f64(1.0).to_i32(), 1);
619        assert_eq!(Fixed::from_f64(1.5).to_i32(), 2);
620        assert_eq!(F26Dot6::from_f64(1.0).to_i32(), 1);
621        assert_eq!(F26Dot6::from_f64(1.5).to_i32(), 2);
622    }
623
624    #[test]
625    fn fixed_from_int() {
626        assert_eq!(Fixed::from_i32(1000).to_bits(), 1000 << 16);
627        assert_eq!(F26Dot6::from_i32(1000).to_bits(), 1000 << 6);
628    }
629
630    #[test]
631    fn fixed_to_f26dot6() {
632        assert_eq!(Fixed::from_f64(42.5).to_f26dot6(), F26Dot6::from_f64(42.5));
633    }
634
635    #[test]
636    fn fixed_muldiv() {
637        assert_eq!(
638            Fixed::from_f64(0.5) * Fixed::from_f64(2.0),
639            Fixed::from_f64(1.0)
640        );
641        assert_eq!(
642            Fixed::from_f64(0.5) / Fixed::from_f64(2.0),
643            Fixed::from_f64(0.25)
644        );
645    }
646
647    // OSS Fuzz caught panic with overflow in fixed point division.
648    // See <https://oss-fuzz.com/testcase-detail/5666843647082496> and
649    // <https://issues.oss-fuzz.com/issues/443104630>
650    #[test]
651    fn fixed_div_neg_overflow() {
652        let a = Fixed::from_f64(-92.5);
653        let b = Fixed::from_f64(0.0028228759765625);
654        // Just don't panic with overflow
655        let _ = a / b;
656    }
657
658    #[test]
659    fn fixed_mul_div_neg_overflow() {
660        let a = Fixed::from_f64(-92.5);
661        let b = Fixed::from_f64(0.0028228759765625);
662        // Just don't panic with overflow
663        let _ = a.mul_div(Fixed::ONE, b);
664    }
665
666    #[test]
667    fn fixed_div_min_value() {
668        // i32::MIN.abs() overflows i32, unsigned_abs() handles this correctly
669        let min = Fixed(i32::MIN);
670        let one = Fixed::ONE;
671        // Just don't panic with overflow
672        let _ = min / one;
673        // Dividing by -1 is also an edge case
674        let neg_one = Fixed(-Fixed::ONE.0);
675        let _ = min / neg_one;
676    }
677
678    #[test]
679    fn fixed_abs_min_value() {
680        // Just don't panic with overflow; we use wrapping arithmetic to
681        // match FT.
682        assert_eq!(Fixed(i32::MIN).abs(), Fixed(i32::MIN));
683    }
684
685    #[test]
686    fn fixed_neg_min_value() {
687        // Just don't panic with overflow; we use wrapping arithmetic to
688        // match FT.
689        assert_eq!(-Fixed(i32::MIN), Fixed(i32::MIN));
690    }
691
692    #[test]
693    fn f48dot16_floats() {
694        assert_eq!(F48Dot16(0x0001_0000), F48Dot16::from_f64(1.0));
695        assert_eq!(F48Dot16(0x0000_0000), F48Dot16::from_f64(0.0));
696        assert_eq!(F48Dot16(0x0001_8000), F48Dot16::from_f64(1.5));
697        assert_eq!(F48Dot16(-0x0001_8000), F48Dot16::from_f64(-1.5));
698        assert_eq!(F48Dot16(0x0000_0001), F48Dot16::EPSILON);
699        // Values far outside the 16.16 range are representable.
700        assert_eq!(
701            F48Dot16((1i64 << 40) * 65536),
702            F48Dot16::from_f64((1u64 << 40) as f64)
703        );
704        assert_eq!(
705            F48Dot16::from_f64(1099511627776.5).to_f64(),
706            1099511627776.5
707        );
708    }
709
710    #[test]
711    fn f48dot16_round() {
712        assert_eq!(F48Dot16(0x0001_7FFF).round(), F48Dot16(0x0001_0000));
713        assert_eq!(F48Dot16(0x0001_8000).round(), F48Dot16(0x0002_0000));
714        assert_eq!(F48Dot16::from_f64(-1.5).round(), F48Dot16::from_f64(-1.0));
715    }
716
717    #[test]
718    fn f48dot16_to_int() {
719        assert_eq!(F48Dot16::from_f64(1.0).to_i64(), 1);
720        assert_eq!(F48Dot16::from_f64(1.5).to_i64(), 2);
721        assert_eq!(F48Dot16::from_f64(-1.5).to_i64(), -1);
722        assert_eq!(F48Dot16::from_f64(1099511627776.25).to_i64(), 1099511627776);
723    }
724
725    #[test]
726    fn f48dot16_from_int() {
727        assert_eq!(F48Dot16::from_i64(1000).to_bits(), 1000 << 16);
728        assert_eq!(F48Dot16::from_i64(1 << 40).to_bits(), 1 << 56);
729    }
730
731    /// Widening from 16.16 is exact over the whole range, and narrowing
732    /// returns exactly when the value fits.
733    #[test]
734    fn f48dot16_fixed_conversions() {
735        for bits in [0, 1, -1, 0x1234_5678, i32::MAX, i32::MIN] {
736            let fixed = Fixed(bits);
737            let wide = fixed.to_f48dot16();
738            assert_eq!(wide.to_bits(), bits as i64);
739            assert_eq!(wide.to_fixed(), fixed);
740        }
741        // Out of range narrows truncate, wrapping like the sibling
742        // conversions rather than saturating.
743        assert_eq!(F48Dot16(i32::MAX as i64 + 1).to_fixed(), Fixed(i32::MIN));
744        assert_eq!(F48Dot16(i32::MIN as i64 - 1).to_fixed(), Fixed(i32::MAX));
745    }
746
747    #[test]
748    fn f48dot16_mul_i32() {
749        assert_eq!(Fixed::from_f64(0.5).mul_i32(3), F48Dot16::from_f64(1.5));
750        assert_eq!(Fixed::ONE.mul_i32(-7), F48Dot16::from_i64(-7));
751        assert_eq!(Fixed::ZERO.mul_i32(i32::MAX), F48Dot16::ZERO);
752        // The extreme product fits without overflow: |i32::MIN| * |i32::MIN|
753        // is 2^62, inside i64.
754        assert_eq!(Fixed(i32::MIN).mul_i32(i32::MIN), F48Dot16(1i64 << 62));
755    }
756
757    /// The variation delta shape: raw deltas scaled and summed exactly,
758    /// then rounded once at the end.
759    #[test]
760    fn f48dot16_delta_accumulation() {
761        let deltas = [100i32, -250, 37];
762        let scalars = [
763            Fixed::from_f64(1.0),
764            Fixed::from_f64(0.5),
765            Fixed::from_f64(0.25),
766        ];
767        let mut accum = F48Dot16::ZERO;
768        for (delta, scalar) in deltas.iter().zip(&scalars) {
769            accum += scalar.mul_i32(*delta);
770        }
771        // 100 - 125 + 9.25 = -15.75, which rounds to the nearest integer.
772        assert_eq!(accum, F48Dot16::from_f64(-15.75));
773        assert_eq!(accum.to_i64(), -16);
774        assert_eq!(accum.round(), F48Dot16::from_f64(-16.0));
775    }
776
777    /// The delta application methods must agree exactly with the former
778    /// `FloatItemDeltaTarget` impls in read-fonts that they replaced, which
779    /// computed base + raw * (1 / quantum) through an f64 raw count.
780    #[test]
781    fn f48dot16_apply_delta() {
782        let raws = [0i64, 1, -1, 100, -32768, 65536, 1 << 40, -(1 << 40)];
783        for raw_bits in raws {
784            let delta = F48Dot16(raw_bits);
785            let raw = delta.to_f64();
786            // Fixed targets scale by 1/65536.
787            let base = Fixed::from_f64(1.5);
788            assert_eq!(
789                base.apply_delta(delta),
790                base.to_f32() + (raw * (1.0 / 65536.0)) as f32
791            );
792            // 2.14 targets scale by 1/16384.
793            let base = F2Dot14::from_f32(0.25);
794            assert_eq!(
795                base.apply_delta(delta),
796                base.to_f32() + (raw * (1.0 / 16384.0)) as f32
797            );
798        }
799        // Spot values: one quantum of delta moves a target by one quantum.
800        assert_eq!(
801            Fixed::ZERO.apply_delta(F48Dot16::from_i64(1)),
802            1.0 / 65536.0
803        );
804        assert_eq!(
805            F2Dot14::from_f32(0.0).apply_delta(F48Dot16::from_i64(1)),
806            1.0 / 16384.0
807        );
808        assert_eq!(
809            F2Dot14::from_f32(0.5).apply_delta(F48Dot16::from_f64(-0.5)),
810            0.5 - 0.5 / 16384.0
811        );
812    }
813
814    #[test]
815    fn f26dot6_muldiv() {
816        assert_eq!(
817            F26Dot6::from_f64(0.5) * F26Dot6::from_f64(2.0),
818            F26Dot6::from_f64(1.0)
819        );
820        assert_eq!(
821            F26Dot6::from_f64(0.5) * F26Dot6::from_f64(-2.4),
822            F26Dot6::from_f64(-1.2)
823        );
824        assert_eq!(F26Dot6::ONE * F26Dot6::ONE, F26Dot6::ONE);
825        assert_eq!(
826            F26Dot6::from_f64(0.5) / F26Dot6::from_f64(2.0),
827            F26Dot6::from_f64(0.25)
828        );
829        assert_eq!(
830            F26Dot6::from_f64(0.5) / F26Dot6::from_f64(-2.4),
831            F26Dot6::from_f64(-0.20833333333333334)
832        );
833        assert_eq!(
834            F26Dot6::from_f64(2.0) / F26Dot6::from_f64(3.0),
835            F26Dot6::from_f64(0.6666666666666666)
836        );
837        assert_eq!(F26Dot6::ONE / F26Dot6::ONE, F26Dot6::ONE);
838        assert_eq!(F26Dot6::ONE / F26Dot6::ZERO, F26Dot6(0x7FFFFFFF));
839        assert_eq!(-F26Dot6::ONE / F26Dot6::ZERO, F26Dot6(-0x7FFFFFFF));
840    }
841
842    #[cfg(feature = "serde")]
843    mod serde {
844        use super::*;
845
846        macro_rules! roundtrip_one {
847            ($fixed:ident) => {{
848                let before = <$fixed>::ONE;
849                let serialized = ::serde_json::to_string(&before).expect("should serialize");
850                assert_eq!(&serialized, "1.0");
851                let after = ::serde_json::from_str(&serialized).expect("should deserialize");
852                assert_eq!(before, after);
853            }};
854        }
855
856        #[test]
857        fn one_is_one_f2dot14() {
858            roundtrip_one!(F2Dot14);
859        }
860
861        #[test]
862        fn one_is_one_f4dot12() {
863            roundtrip_one!(F4Dot12);
864        }
865
866        #[test]
867        fn one_is_one_f6dot10() {
868            roundtrip_one!(F6Dot10);
869        }
870
871        #[test]
872        fn one_is_one_fixed() {
873            roundtrip_one!(Fixed);
874        }
875
876        #[test]
877        fn one_is_one_f26dot6() {
878            roundtrip_one!(F26Dot6);
879        }
880
881        macro_rules! roundtrip_all {
882            ($fixed:ident, $ty:ty) => {
883                for raw in <$ty>::MIN..=<$ty>::MAX {
884                    let fixed = $fixed(raw);
885                    let fixed_float = fixed.to_f64();
886
887                    let json_value = ::serde_json::to_value(&fixed).expect("should serialize");
888                    let json_float = json_value
889                        .as_f64()
890                        .expect("serde didn't serialize the value to a float");
891
892                    // Normally directly comparing floats is flawed, but these
893                    // should have been converted to float using the exact same
894                    // method each, so I wouldn't expect them to be different
895                    assert_eq!(
896                        fixed_float,
897                        json_float,
898                        "failed on {raw} ({fixed_type}({raw:#X})): {json_float} != {fixed_float}",
899                        fixed_type = ::std::stringify!($fixed),
900                    );
901                }
902            };
903        }
904
905        #[test]
906        fn roundtrip_all_f2dot14() {
907            roundtrip_all!(F2Dot14, i16);
908        }
909
910        #[test]
911        fn roundtrip_all_f4dot12() {
912            roundtrip_all!(F4Dot12, i16);
913        }
914
915        #[test]
916        fn roundtrip_all_f6dot10() {
917            roundtrip_all!(F6Dot10, i16);
918        }
919
920        #[test]
921        #[ignore = "enumerating all i32 values takes a while"]
922        fn roundtrip_all_fixed() {
923            roundtrip_all!(Fixed, i32);
924        }
925
926        #[test]
927        #[ignore = "enumerating all i32 values takes a while"]
928        fn roundtrip_all_f26dot6() {
929            roundtrip_all!(F26Dot6, i32);
930        }
931    }
932}