Skip to content

Commit 1047383

Browse files
committed
bug fix fixpnt conversion of real values smaller than half of minpos
1 parent e68802f commit 1047383

File tree

3 files changed

+156
-146
lines changed

3 files changed

+156
-146
lines changed

include/universal/number/fixpnt/fixpnt_impl.hpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -703,10 +703,12 @@ class fixpnt {
703703
uint64_t bits{ 0 };
704704
extractFields(v, s, unbiasedExponent, fraction, bits); // use native conversion
705705
if (unbiasedExponent > 0) fraction |= (1ull << ieee754_parameter<Arith>::fbits);
706-
int radixPoint = ieee754_parameter<Arith>::fbits - (static_cast<int>(unbiasedExponent) - ieee754_parameter<Arith>::bias);
706+
int biasedExponent = static_cast<int>(unbiasedExponent) - ieee754_parameter<Arith>::bias;
707+
int radixPoint = ieee754_parameter<Arith>::fbits - biasedExponent;
707708

708709
// our fixed-point has its radixPoint at rbits
709710
int shiftRight = std::min(radixPoint - int(rbits), 64);
711+
if (shiftRight > (ieee754_parameter<Arith>::fbits + 1)) return f; // return zero
710712
if (shiftRight > 0) {
711713
// we need to round the raw bits
712714
// collect guard, round, and sticky bits

static/fixpnt/binary/conversion/mod_conversion.cpp

+152-145
Original file line numberDiff line numberDiff line change
@@ -17,156 +17,163 @@
1717
#include <universal/number/fixpnt/fixpnt.hpp>
1818
#include <universal/verification/fixpnt_test_suite.hpp>
1919

20-
// generate specific test case that you can trace with the trace conditions in fixpnt.hpp
21-
// for most bugs they are traceable with _trace_conversion and _trace_add
22-
template<size_t nbits, size_t rbits, typename Ty>
23-
void GenerateTestCase(Ty _a, Ty _b) {
24-
Ty ref;
25-
sw::universal::fixpnt<nbits, rbits> a, b, cref, result;
26-
a = _a;
27-
b = _b;
28-
result = a + b;
29-
ref = _a + _b;
30-
cref = ref;
31-
std::streamsize oldPrecision = std::cout.precision();
32-
std::cout << std::setprecision(nbits - 2);
33-
std::cout << std::setw(nbits) << _a << " + " << std::setw(nbits) << _b << " = " << std::setw(nbits) << ref << std::endl;
34-
std::cout << a << " + " << b << " = " << result << " (reference: " << cref << ") " ;
35-
std::cout << (cref == result ? "PASS" : "FAIL") << std::endl << std::endl;
36-
std::cout << std::dec << std::setprecision(oldPrecision);
37-
}
38-
39-
template<size_t nbits, size_t rbits>
40-
void GenerateFixedPointComparisonTable() {
41-
using namespace sw::universal;
42-
constexpr size_t NR_VALUES = (size_t(1) << nbits);
43-
fixpnt<nbits, rbits> fp;
44-
fixpnt<nbits+1, rbits+1> fpnext;
45-
std::cout << " fixpnt<" << nbits + 1 << "," << rbits + 1 << "> | fixpnt<" << nbits << ", " << rbits << ">" << '\n';
46-
for (size_t i = 0; i < NR_VALUES; ++i) {
47-
fp.set_raw_bits(i);
48-
fpnext.set_raw_bits(2*i);
49-
std::cout << to_binary(fpnext) << ' ' << std::setw(10) << fpnext << " | " << to_binary(fp) << ' ' << std::setw(15) << fp << '\n';
50-
fpnext.set_raw_bits(2 * i + 1);
51-
std::cout << to_binary(fpnext) << ' ' << std::setw(10) << fpnext << " | " << '\n';
52-
}
53-
}
54-
55-
// verify that integer conversion picks up the correct integer encoding for the fixed-point
56-
template<size_t nbits, size_t rbits, bool arithmetic, typename bt>
57-
int VerifySignedIntegerProgressions(bool reportTestCases) {
58-
using namespace sw::universal;
59-
int nrOfFailedTestCases = 0;
20+
namespace sw {
21+
namespace universal {
22+
23+
// generate specific test case that you can trace with the trace conditions in fixpnt.hpp
24+
// for most bugs they are traceable with _trace_conversion and _trace_add
25+
template<size_t nbits, size_t rbits, typename Ty>
26+
void GenerateTestCase(Ty _a, Ty _b) {
27+
Ty ref;
28+
sw::universal::fixpnt<nbits, rbits> a, b, cref, result;
29+
a = _a;
30+
b = _b;
31+
result = a + b;
32+
ref = _a + _b;
33+
cref = ref;
34+
std::streamsize oldPrecision = std::cout.precision();
35+
std::cout << std::setprecision(nbits - 2);
36+
std::cout << std::setw(nbits) << _a << " + " << std::setw(nbits) << _b << " = " << std::setw(nbits) << ref << std::endl;
37+
std::cout << a << " + " << b << " = " << result << " (reference: " << cref << ") " ;
38+
std::cout << (cref == result ? "PASS" : "FAIL") << std::endl << std::endl;
39+
std::cout << std::dec << std::setprecision(oldPrecision);
40+
}
6041

61-
// generate the integer progression for this fixpnt, which is represented by a marching MSB
62-
constexpr size_t ibits = nbits - rbits; // <8,4> has 8-4 = 4 ibits in 2's complement form, and 4 rbits
63-
static_assert(ibits > 2, "test requires at least 3 bits of integer bits");
64-
// assume that we have maximally 64 integer bits
65-
static_assert(ibits < 65, "test assumes we have at most 64 integer bits");
66-
67-
// largest negative integer is 100...000
68-
// largest positive integer is 011111111
69-
// largest positive power of 2 is 010000000
70-
71-
// Fixed maxneg(SpecificValue::maxneg);
72-
std::uint64_t maxneg{0xFFFFFFFFFFFFFFFFull};
73-
maxneg <<= (ibits - 1);
74-
int64_t marchingOne = (long long)maxneg;
75-
std::cout << "ibits - 1 = " << (ibits - 1) << '\n';
76-
std::cout << "maxneg " << to_binary(maxneg) << '\n';
77-
std::cout << "marchingOne " << to_binary(marchingOne) << '\n';
78-
for (int i = static_cast<int>(ibits - 1); i >= 0; --i) {
79-
fixpnt<nbits, rbits, arithmetic, bt> a = marchingOne;
80-
if (i == 0) {
81-
if (reportTestCases) std::cout << "i = " << std::setw(3) << 0 << " bit pattern: " << to_binary(marchingOne) << " : " << to_binary(a) << " : " << a << '\n';
42+
template<size_t nbits, size_t rbits>
43+
void GenerateFixedPointComparisonTable() {
44+
using namespace sw::universal;
45+
constexpr size_t NR_VALUES = (size_t(1) << nbits);
46+
fixpnt<nbits, rbits> fp;
47+
fixpnt<nbits+1, rbits+1> fpnext;
48+
std::cout << " fixpnt<" << nbits + 1 << "," << rbits + 1 << "> | fixpnt<" << nbits << ", " << rbits << ">" << '\n';
49+
for (size_t i = 0; i < NR_VALUES; ++i) {
50+
fp.set_raw_bits(i);
51+
fpnext.set_raw_bits(2*i);
52+
std::cout << to_binary(fpnext) << ' ' << std::setw(10) << fpnext << " | " << to_binary(fp) << ' ' << std::setw(15) << fp << '\n';
53+
fpnext.set_raw_bits(2 * i + 1);
54+
std::cout << to_binary(fpnext) << ' ' << std::setw(10) << fpnext << " | " << '\n';
55+
}
8256
}
83-
else {
84-
if (reportTestCases) std::cout << "i = " << std::setw(3) << -i << " bit pattern: " << to_binary(marchingOne) << " : " << to_binary(a) << " : " << a << '\n';
57+
58+
// verify that integer conversion picks up the correct integer encoding for the fixed-point
59+
template<size_t nbits, size_t rbits, bool arithmetic, typename bt>
60+
int VerifySignedIntegerProgressions(bool reportTestCases) {
61+
using namespace sw::universal;
62+
int nrOfFailedTestCases = 0;
63+
64+
// generate the integer progression for this fixpnt, which is represented by a marching MSB
65+
constexpr size_t ibits = nbits - rbits; // <8,4> has 8-4 = 4 ibits in 2's complement form, and 4 rbits
66+
static_assert(ibits > 2, "test requires at least 3 bits of integer bits");
67+
// assume that we have maximally 64 integer bits
68+
static_assert(ibits < 65, "test assumes we have at most 64 integer bits");
69+
70+
// largest negative integer is 100...000
71+
// largest positive integer is 011111111
72+
// largest positive power of 2 is 010000000
73+
74+
// Fixed maxneg(SpecificValue::maxneg);
75+
std::uint64_t maxneg{0xFFFFFFFFFFFFFFFFull};
76+
maxneg <<= (ibits - 1);
77+
int64_t marchingOne = (long long)maxneg;
78+
std::cout << "ibits - 1 = " << (ibits - 1) << '\n';
79+
std::cout << "maxneg " << to_binary(maxneg) << '\n';
80+
std::cout << "marchingOne " << to_binary(marchingOne) << '\n';
81+
for (int i = static_cast<int>(ibits - 1); i >= 0; --i) {
82+
fixpnt<nbits, rbits, arithmetic, bt> a = marchingOne;
83+
if (i == 0) {
84+
if (reportTestCases) std::cout << "i = " << std::setw(3) << 0 << " bit pattern: " << to_binary(marchingOne) << " : " << to_binary(a) << " : " << a << '\n';
85+
}
86+
else {
87+
if (reportTestCases) std::cout << "i = " << std::setw(3) << -i << " bit pattern: " << to_binary(marchingOne) << " : " << to_binary(a) << " : " << a << '\n';
88+
}
89+
if (a != marchingOne) ++nrOfFailedTestCases;
90+
marchingOne /= 2;
91+
}
92+
marchingOne = 1;
93+
for (size_t i = 1; i < ibits; ++i) {
94+
fixpnt<nbits, rbits, arithmetic, bt> a = marchingOne;
95+
if (reportTestCases) std::cout << "i = " << std::setw(3) << i << " bit pattern: " << to_binary(marchingOne) << " : " << to_binary(a) << " : " << a << '\n';
96+
if (a != marchingOne) ++nrOfFailedTestCases;
97+
marchingOne *= 2;
98+
}
99+
return nrOfFailedTestCases;
85100
}
86-
if (a != marchingOne) ++nrOfFailedTestCases;
87-
marchingOne /= 2;
88-
}
89-
marchingOne = 1;
90-
for (size_t i = 1; i < ibits; ++i) {
91-
fixpnt<nbits, rbits, arithmetic, bt> a = marchingOne;
92-
if (reportTestCases) std::cout << "i = " << std::setw(3) << i << " bit pattern: " << to_binary(marchingOne) << " : " << to_binary(a) << " : " << a << '\n';
93-
if (a != marchingOne) ++nrOfFailedTestCases;
94-
marchingOne *= 2;
95-
}
96-
return nrOfFailedTestCases;
97-
}
98101

99-
template<size_t nbits, size_t rbits, bool arithmetic, typename bt>
100-
int VerifyUnsignedIntegerProgressions(bool reportTestCases) {
101-
using namespace sw::universal;
102-
int nrOfFailedTestCases = 0;
102+
template<size_t nbits, size_t rbits, bool arithmetic, typename bt>
103+
int VerifyUnsignedIntegerProgressions(bool reportTestCases) {
104+
using namespace sw::universal;
105+
int nrOfFailedTestCases = 0;
106+
107+
using Fixed = fixpnt<nbits, rbits, arithmetic, bt>;
108+
// generate the integer progression for this fixpnt, which is represented by a marching MSB
109+
constexpr size_t ibits = nbits - rbits; // <8,4> has 8-4 = 4 ibits in 2's complement form, and 4 rbits
110+
static_assert(ibits > 2, "test requires at least 3 bits of integer bits");
111+
// assume that we have maximally 64 integer bits
112+
static_assert(ibits < 65, "test assumes we have at most 64 integer bits");
113+
114+
Fixed a;
115+
uint64_t marchingOne = 1;
116+
for (size_t i = 1; i < ibits; ++i) {
117+
a = marchingOne;
118+
if (reportTestCases) std::cout << "i = " << std::setw(3) << i << " bit pattern: " << to_binary(marchingOne) << " : " << to_binary(a) << '\n';
119+
if (a != marchingOne) {
120+
++nrOfFailedTestCases;
121+
}
122+
marchingOne *= 2;
123+
}
124+
return nrOfFailedTestCases;
125+
}
103126

104-
using Fixed = fixpnt<nbits, rbits, arithmetic, bt>;
105-
// generate the integer progression for this fixpnt, which is represented by a marching MSB
106-
constexpr size_t ibits = nbits - rbits; // <8,4> has 8-4 = 4 ibits in 2's complement form, and 4 rbits
107-
static_assert(ibits > 2, "test requires at least 3 bits of integer bits");
108-
// assume that we have maximally 64 integer bits
109-
static_assert(ibits < 65, "test assumes we have at most 64 integer bits");
110-
111-
Fixed a;
112-
uint64_t marchingOne = 1;
113-
for (size_t i = 1; i < ibits; ++i) {
114-
a = marchingOne;
115-
if (reportTestCases) std::cout << "i = " << std::setw(3) << i << " bit pattern: " << to_binary(marchingOne) << " : " << to_binary(a) << '\n';
116-
if (a != marchingOne) {
117-
++nrOfFailedTestCases;
127+
template<size_t nbits, size_t rbits, bool arithmetic, typename bt>
128+
int VerifySignedIntegerProgressionsFloat(bool reportTestCases) {
129+
using namespace sw::universal;
130+
int nrOfFailedTestCases = 0;
131+
132+
using Fixed = fixpnt<nbits, rbits, arithmetic, bt>;
133+
// generate the integer progression for this fixpnt, which is represented by a marging MSB
134+
constexpr size_t ibits = nbits - rbits; // <8,4> has 8-4 = 4 ibits in 2's complement form, and 4 rbits
135+
// assume that we have maximally 64 integer bits
136+
static_assert(ibits < 65, "test assumes we have at most 64 integer bits");
137+
138+
// largest negative integer is 100...000
139+
// largest positive integer is 011111111
140+
// largest positive power of 2 is 010000000
141+
142+
Fixed a, b;
143+
144+
constexpr Fixed maxneg(SpecificValue::maxneg);
145+
int64_t marchingOne = (long long)maxneg;
146+
float f = float(marchingOne);
147+
std::cout << to_binary(f) << '\n';
148+
for (int i = static_cast<int>(ibits - 1); i >= 0; --i) {
149+
a = float(marchingOne);
150+
b = double(marchingOne);
151+
if (i == 0) {
152+
if (reportTestCases) std::cout << "i = " << std::setw(3) << 0 << " bit pattern: " << to_binary(marchingOne) << " : " << to_binary(a) << " : " << to_binary(b) << '\n';
153+
}
154+
else {
155+
if (reportTestCases) std::cout << "i = " << std::setw(3) << -i << " bit pattern: " << to_binary(marchingOne) << " : " << to_binary(a) << " : " << to_binary(b) << '\n';
156+
}
157+
if (a != marchingOne || b != marchingOne) {
158+
++nrOfFailedTestCases;
159+
}
160+
marchingOne /= 2;
161+
}
162+
marchingOne = 1;
163+
for (size_t i = 1; i < ibits; ++i) {
164+
a = float(marchingOne);
165+
b = double(marchingOne);
166+
if (reportTestCases) std::cout << "i = " << std::setw(3) << i << " bit pattern: " << to_binary(marchingOne) << " : " << to_binary(a) << " : " << to_binary(b) << '\n';
167+
if (a != marchingOne || b != marchingOne) {
168+
++nrOfFailedTestCases;
169+
}
170+
marchingOne *= 2;
171+
}
172+
return nrOfFailedTestCases;
118173
}
119-
marchingOne *= 2;
120-
}
121-
return nrOfFailedTestCases;
122-
}
123174

124-
template<size_t nbits, size_t rbits, bool arithmetic, typename bt>
125-
int VerifySignedIntegerProgressionsFloat(bool reportTestCases) {
126-
using namespace sw::universal;
127-
int nrOfFailedTestCases = 0;
128175

129-
using Fixed = fixpnt<nbits, rbits, arithmetic, bt>;
130-
// generate the integer progression for this fixpnt, which is represented by a marging MSB
131-
constexpr size_t ibits = nbits - rbits; // <8,4> has 8-4 = 4 ibits in 2's complement form, and 4 rbits
132-
// assume that we have maximally 64 integer bits
133-
static_assert(ibits < 65, "test assumes we have at most 64 integer bits");
134-
135-
// largest negative integer is 100...000
136-
// largest positive integer is 011111111
137-
// largest positive power of 2 is 010000000
138-
139-
Fixed a, b;
140-
141-
constexpr Fixed maxneg(SpecificValue::maxneg);
142-
int64_t marchingOne = (long long)maxneg;
143-
float f = float(marchingOne);
144-
std::cout << to_binary(f) << '\n';
145-
for (int i = static_cast<int>(ibits - 1); i >= 0; --i) {
146-
a = float(marchingOne);
147-
b = double(marchingOne);
148-
if (i == 0) {
149-
if (reportTestCases) std::cout << "i = " << std::setw(3) << 0 << " bit pattern: " << to_binary(marchingOne) << " : " << to_binary(a) << " : " << to_binary(b) << '\n';
150-
}
151-
else {
152-
if (reportTestCases) std::cout << "i = " << std::setw(3) << -i << " bit pattern: " << to_binary(marchingOne) << " : " << to_binary(a) << " : " << to_binary(b) << '\n';
153-
}
154-
if (a != marchingOne || b != marchingOne) {
155-
++nrOfFailedTestCases;
156-
}
157-
marchingOne /= 2;
158176
}
159-
marchingOne = 1;
160-
for (size_t i = 1; i < ibits; ++i) {
161-
a = float(marchingOne);
162-
b = double(marchingOne);
163-
if (reportTestCases) std::cout << "i = " << std::setw(3) << i << " bit pattern: " << to_binary(marchingOne) << " : " << to_binary(a) << " : " << to_binary(b) << '\n';
164-
if (a != marchingOne || b != marchingOne) {
165-
++nrOfFailedTestCases;
166-
}
167-
marchingOne *= 2;
168-
}
169-
return nrOfFailedTestCases;
170177
}
171178

172179
// Regression testing guards: typically set by the cmake configuration, but MANUAL_TESTING is an override
@@ -198,8 +205,8 @@ try {
198205

199206
#if MANUAL_TESTING
200207

201-
fixpnt<4, 1, Modulo, uint8_t> f;
202-
f = 0.25f;
208+
fixpnt<8, 4, Modulo, uint16_t> f;
209+
f = 0.000001f;
203210
std::cout << to_binary(f) << " : " << f << std::endl;
204211

205212
// fixpnt_range(fixpnt< 4, 0, Modulo, uint16_t>());
@@ -226,8 +233,8 @@ try {
226233
// nrOfFailedTestCases += ReportTestResult(VerifySignedIntegerProgressionsFloat<128, 64, Modulo, uint8_t>(reportTestCases);
227234

228235
// nrOfFailedTestCases += ReportTestResult(VerifyUnsignedIntegerProgressions< 8, 4, Modulo, uint8_t>(reportTestCases), test_tag, "fixpnt< 8, 4, Modulo, uint8_t>");
229-
nrOfFailedTestCases += ReportTestResult(VerifyUnsignedIntegerProgressions< 16, 8, Modulo, uint8_t>(true), test_tag, "fixpnt< 16, 8, Modulo, uint8_t>");
230-
nrOfFailedTestCases += ReportTestResult(VerifyUnsignedIntegerProgressions< 32, 16, Modulo, uint8_t>(reportTestCases), test_tag, "fixpnt< 32,16, Modulo, uint8_t>");
236+
// nrOfFailedTestCases += ReportTestResult(VerifyUnsignedIntegerProgressions< 16, 8, Modulo, uint8_t>(true), test_tag, "fixpnt< 16, 8, Modulo, uint8_t>");
237+
// nrOfFailedTestCases += ReportTestResult(VerifyUnsignedIntegerProgressions< 32, 16, Modulo, uint8_t>(reportTestCases), test_tag, "fixpnt< 32,16, Modulo, uint8_t>");
231238
// nrOfFailedTestCases += ReportTestResult(VerifyUnsignedIntegerProgressions< 64, 32, Modulo, uint8_t>(reportTestCases), test_tag, "fixpnt< 64,32, Modulo, uint8_t>");
232239
// nrOfFailedTestCases += ReportTestResult(VerifyUnsignedIntegerProgressions<128, 64, Modulo, uint8_t>(reportTestCases), test_tag, "fixpnt<128,64, Modulo, uint8_t>");
233240

tools/cmd/fixpnt.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// SPDX-License-Identifier: MIT
55
//
66
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
7+
#include <universal/utility/directives.hpp>
78
#include <iostream>
89
#include <iomanip>
910
#include <typeinfo>

0 commit comments

Comments
 (0)