xref: /imagick/util/functions.php (revision 06116aa2)
1<?php
2
3// Mirrored from https://github.com/Danack/HexFloat
4
5require_once __DIR__ . "/FloatInfo.php";
6require_once __DIR__ . "/Float32Info.php";
7
8use HexFloat\FloatInfo;
9use HexFloat\Float32Info;
10
11/**
12 * Returns a string containing a hexadecimal representation of the given float,
13 * using 64 bits of info
14 *
15 * @param float $number
16 * @return string
17 */
18function floathex(float $number)
19{
20    return strrev(unpack('h*', pack('d', $number))[1]);
21}
22
23/**
24 * Returns a string containing a hexadecimal representation of the given float,
25 * using 32 bits of info
26 *
27 * @param float $number
28 * @return string
29 */
30function floathex32(float $num)
31{
32    return strrev(unpack('h*', pack('f', $num))[1]);
33}
34
35/**
36 * Convert a floating point number to a FloatInfo object,
37 * which contains string representations of the float's sign,
38 * exponent and mantissa
39 * @param float $num
40 * @return FloatInfo
41 */
42function float_info(float $num)
43{
44    $float64 = floathex($num);
45
46    //Sign bit: 1 bit
47    //Exponent: 11 bits
48    //Significand precision: 53 bits (52 explicitly stored)
49
50    $chars = str_split($float64);
51
52
53    // 3 bits from this
54    $byte1 = hexdec($chars[0]);
55    // 4 bits from this
56    $byte2 = hexdec($chars[1]);
57
58    // 1 bit from this
59    $byte3 = hexdec($chars[2]);
60
61    $sign = '0';
62
63    if ($byte1 >= 8) {
64        $sign = '1';
65    }
66
67    $exponentString = substr($float64, 0, 3);
68    $exponentValue = hexdec($exponentString) & 0x7ff;
69    $exponent = sprintf("%b", $exponentValue);
70    $exponent = str_pad($exponent, 11, '0', STR_PAD_LEFT);
71
72    $mantissa = substr($float64, 2);
73    $mantissa = hexdec($mantissa) & 0xfffffffffffff;
74    $mantissa = sprintf("%b", $mantissa);
75    $mantissa = str_pad($mantissa, 52, '0', STR_PAD_LEFT);
76
77    return new FloatInfo(
78        $sign,
79        $exponent,
80        $mantissa
81    );
82}
83
84/**
85 * Convert a floating point number to a Float32Info object,
86 * which contains string representations of the float's sign,
87 * exponent and mantissa
88 *
89 * @param float $num
90 * @return Float32Info
91 */
92function float_info_32(float $num)
93{
94    $float32 = floathex32($num);
95    $chars = str_split($float32);
96
97    // 3 bits from this
98    $byte1 = hexdec($chars[0]);
99    // 4 bits from this
100    $byte2 = hexdec($chars[1]);
101
102    // 1 bit from this
103    $byte3 = hexdec($chars[2]);
104
105    $sign = '0';
106
107    if ($byte1 >= 8) {
108        $sign = '1';
109    }
110    $exponent3Bits = ($byte1 & 0x7);
111    $exponent4Bits = $byte2;
112    $exponent1Bit = ($byte3 & 0x8) >> 3;
113    $exponent = ($exponent3Bits << 5) | ($exponent4Bits << 1) | $exponent1Bit;
114
115    $exponent = sprintf("%b", $exponent);
116    $exponent = str_pad($exponent, 8, '0', STR_PAD_LEFT);
117
118    $mantissa = substr($float32, 2, 6);
119    $mantissa = hexdec($mantissa) & 0x7fffff;
120    $mantissa = sprintf("%b", $mantissa);
121    $mantissa = str_pad($mantissa, 23, '0', STR_PAD_LEFT);
122
123    return new Float32Info(
124        $sign,
125        $exponent,
126        $mantissa
127    );
128}
129
130/**
131 * Produce a debug string that shows the Sign, Exponent and Mantissa for
132 * two floating point numbers, using 64bit precision
133 *
134 *
135 * @param float $value1
136 * @param float $value2
137 * @return string
138 *
139 * Example result
140 * ┌──────┬─────────────┬──────────────────────────────────────────────────────┐
141 * │ Sign │ Exponent    │ Mantissa                                             │
142 * │    0 │ 01111111011 │ 1001100110011001100110011001100110011001100110011010 │
143 * │    0 │ 10000011001 │ 0111110101111000010000000100000000000000000000000000 │
144 * └──────┴─────────────┴──────────────────────────────────────────────────────┘
145 *
146 */
147function float_compare(float $value1, float $value2)
148{
149    $float_info_1 = float_info($value1);
150    $float_info_2 = float_info($value2);
151
152    //Sign bit: 1 bit
153    //Exponent: 11 bits
154    //Significand precision: 53 bits (52 explicitly stored)
155
156    $output  = "┌──────┬─────────────┬──────────────────────────────────────────────────────┐\n";
157    $output .= "│ Sign │ Exponent    │ Mantissa                                             │\n";
158
159    $format_string = "│    %s │ %s │ %s │\n";
160
161    $output .= sprintf($format_string, $float_info_1->getSign(), $float_info_1->getExponent(), $float_info_1->getMantissa());
162    $output .= sprintf($format_string, $float_info_2->getSign(), $float_info_2->getExponent(), $float_info_2->getMantissa());
163
164    $output .= "└──────┴─────────────┴──────────────────────────────────────────────────────┘\n";
165
166    return $output;
167}
168
169
170/**
171 * Produce a debug string that shows the Sign, Exponent and Mantissa for
172 * two floating point numbers, using 32bit precision
173 *
174 * @param float $value1
175 * @param float $value2
176 * @return string
177 *
178 * Example result
179 * ┌──────┬──────────┬─────────────────────────┐
180 * │ Sign │ Exponent │ Mantissa                │
181 * │    0 │ 01111011 │ 10011001100110011001101 │
182 * │    0 │ 10011001 │ 01111101011110000100000 │
183 * └──────┴──────────┴─────────────────────────┘
184 *
185 */
186function float_compare_32(float $value1, float $value2)
187{
188    $float_info_1 = float_info_32($value1);
189    $float_info_2 = float_info_32($value2);
190
191    $output  = "┌──────┬──────────┬─────────────────────────┐\n";
192    $output .= "│ Sign │ Exponent │ Mantissa                │\n";
193
194    $format_string = "│    %s │ %s │ %s │\n";
195
196    $output .= sprintf($format_string, $float_info_1->getSign(), $float_info_1->getExponent(), $float_info_1->getMantissa());
197    $output .= sprintf($format_string, $float_info_2->getSign(), $float_info_2->getExponent(), $float_info_2->getMantissa());
198
199    $output .= "└──────┴──────────┴─────────────────────────┘\n";
200
201    return $output;
202}
203