Skip to content

Commit 7bb18b4

Browse files
committed
fixed signed overflow in ldexpl and implemented scalbln(f/l) in asm
1 parent 8bc9b6a commit 7bb18b4

File tree

6 files changed

+177
-28
lines changed

6 files changed

+177
-28
lines changed

src/libc/ldexpl.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ static long double generate_ldexpl_mult(int expon) {
4242
* subnormal values.
4343
*/
4444
static long double _ldexpl_c_positive(long double x, int expon) {
45+
/* clamps the exponent to avoid signed overflow bugs */
46+
/* hopefully these can also remove call pe, __setflag from the assembly output */
47+
if (expon > 4095) {
48+
expon = 4095;
49+
}
50+
if (expon < -4096) {
51+
expon = -4096;
52+
}
4553
F64_pun val;
4654
val.flt = x;
4755
/* expon == 0 || iszero(x) */

src/libc/scalbln.c

Lines changed: 0 additions & 28 deletions
This file was deleted.

src/libc/scalblnf.src

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
assume adl=1
2+
3+
section .text
4+
5+
public _scalblnf, _scalbln
6+
7+
; float _scalblnf(float, long)
8+
_scalbln:
9+
_scalblnf:
10+
; We need to cast the int32_t scale to int24_t via saturation.
11+
; scalblnf will always overflow/underflow if the scale is larger than +-280.
12+
ld hl, 12
13+
add hl, sp
14+
ld a, (hl)
15+
dec hl
16+
; If bits [16, 23] match bits [24, 31] then we don't need to do anything.
17+
; It implies that the scale is either already [-65536, +65535] and can
18+
; be safely truncated, or that the original and truncated scale values
19+
; are both larger than +-65535 and have the same sign.
20+
.overflow:
21+
cp a, (hl)
22+
jp z, _scalbnf
23+
; We need modify the scale value to ensure that overflow/underflow still occurs.
24+
; Ensure that bits[16, 23] are not all zeros/ones to set the scale to a large magnitude.
25+
or a, $03
26+
dec a
27+
; bit 23 = bit 31, bit 16 is cleared, and bit 17 is set
28+
ld (hl), a ; store the new scale
29+
; cp a, a ; set Z flag
30+
jr .overflow
31+
32+
extern _scalbnf

src/libc/scalblnl.src

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
assume adl=1
2+
3+
section .text
4+
5+
public _scalblnl
6+
7+
; long double _scalblnl(long double, long)
8+
_scalblnl:
9+
; We need to cast the int32_t scale to int24_t via saturation.
10+
; scalblnl will always overflow/underflow if the scale is larger than +-2100.
11+
ld hl, 15
12+
add hl, sp
13+
ld a, (hl)
14+
dec hl
15+
; If bits [16, 23] match bits [24, 31] then we don't need to do anything.
16+
; It implies that the scale is either already [-65536, +65535] and can
17+
; be safely truncated, or that the original and truncated scale values
18+
; are both larger than +-65535 and have the same sign.
19+
.overflow:
20+
cp a, (hl)
21+
jp z, _scalbnl
22+
; We need modify the scale value to ensure that overflow/underflow still occurs.
23+
; Ensure that bits[16, 23] are not all zeros/ones to set the scale to a large magnitude.
24+
or a, $03
25+
dec a
26+
; bit 23 = bit 31, bit 16 is cleared, and bit 17 is set
27+
ld (hl), a ; store the new scale
28+
; cp a, a
29+
jr .overflow
30+
31+
extern _scalbnl

test/floating_point/float32_ldexp/src/main.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#include <stddef.h>
33
#include <stdint.h>
44
#include <stdio.h>
5+
#include <limits.h>
6+
#include <string.h>
57
#include <math.h>
68
#include <fenv.h>
79
#include <errno.h>
@@ -92,8 +94,58 @@ size_t run_test(void) {
9294
return SIZE_MAX;
9395
}
9496

97+
static int32_t rand_i32_expon() {
98+
union {
99+
struct {
100+
uint16_t part[2];
101+
};
102+
int32_t full;
103+
} ret;
104+
ret.part[0] = rand() & 0x03FF;
105+
ret.part[1] = rand() & 0xC0C0;
106+
return ret.full;
107+
}
108+
109+
static float rand_f32() {
110+
union {
111+
struct {
112+
uint16_t part[2];
113+
};
114+
float full;
115+
} ret;
116+
ret.part[0] = rand();
117+
ret.part[1] = rand();
118+
return ret.full;
119+
}
120+
121+
static int clamp_exponent(long expon) {
122+
if (expon > INT_MAX) {
123+
return INT_MAX;
124+
}
125+
if (expon < INT_MIN) {
126+
return INT_MIN;
127+
}
128+
return (int)expon;
129+
}
130+
131+
void run_edge_case(void) {
132+
srand(0x7184CE);
133+
float input, output_i24, output_i32;
134+
for (size_t i = 0; i < 512; i++) {
135+
input = rand_f32();
136+
int32_t expon = rand_i32_expon();
137+
output_i24 = scalbnf(input, clamp_exponent(expon));
138+
output_i32 = scalblnf(input, expon);
139+
if (memcmp(&output_i24, &output_i32, sizeof(float)) != 0) {
140+
fputs("Failed edge case\n", stdout);
141+
return;
142+
}
143+
}
144+
}
145+
95146
int main(void) {
96147
os_ClrHome();
148+
run_edge_case();
97149
size_t fail_index = run_test();
98150
if (fail_index == SIZE_MAX) {
99151
fputs("All tests passed", stdout);

test/floating_point/float64_ldexp/src/main.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#include <stddef.h>
33
#include <stdint.h>
44
#include <stdio.h>
5+
#include <limits.h>
6+
#include <string.h>
57
#include <math.h>
68
#include <assert.h>
79
#include <ti/screen.h>
@@ -40,8 +42,60 @@ size_t run_test(void) {
4042
return SIZE_MAX;
4143
}
4244

45+
static int32_t rand_i32_expon() {
46+
union {
47+
struct {
48+
uint16_t part[2];
49+
};
50+
int32_t full;
51+
} ret;
52+
ret.part[0] = rand() & 0x1FFF;
53+
ret.part[1] = rand() & 0xC0C0;
54+
return ret.full;
55+
}
56+
57+
static long double rand_f64() {
58+
union {
59+
struct {
60+
uint16_t part[4];
61+
};
62+
long double full;
63+
} ret;
64+
ret.part[0] = rand();
65+
ret.part[1] = rand();
66+
ret.part[2] = rand();
67+
ret.part[3] = rand();
68+
return ret.full;
69+
}
70+
71+
static int clamp_exponent(long expon) {
72+
if (expon > INT_MAX) {
73+
return INT_MAX;
74+
}
75+
if (expon < INT_MIN) {
76+
return INT_MIN;
77+
}
78+
return (int)expon;
79+
}
80+
81+
void run_edge_case(void) {
82+
srand(0x7184CE);
83+
long double input, output_i24, output_i32;
84+
for (size_t i = 0; i < 512; i++) {
85+
input = rand_f64();
86+
int32_t expon = rand_i32_expon();
87+
output_i24 = scalbnl(input, clamp_exponent(expon));
88+
output_i32 = scalblnl(input, expon);
89+
if (memcmp(&output_i24, &output_i32, sizeof(long double)) != 0) {
90+
fputs("Failed edge case\n", stdout);
91+
return;
92+
}
93+
}
94+
}
95+
4396
int main(void) {
4497
os_ClrHome();
98+
run_edge_case();
4599
size_t fail_index = run_test();
46100
if (fail_index == SIZE_MAX) {
47101
fputs("All tests passed", stdout);

0 commit comments

Comments
 (0)