Skip to content

Commit b57a40a

Browse files
committed
Add tool floating-point-number-converter.
1 parent f962c41 commit b57a40a

File tree

8 files changed

+313
-0
lines changed

8 files changed

+313
-0
lines changed

components.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ declare module '@vue/runtime-core' {
7979
Encryption: typeof import('./src/tools/encryption/encryption.vue')['default']
8080
EtaCalculator: typeof import('./src/tools/eta-calculator/eta-calculator.vue')['default']
8181
FavoriteButton: typeof import('./src/components/FavoriteButton.vue')['default']
82+
FloatingPointNumberConverter: typeof import('./src/tools/floating-point-number-converter/floating-point-number-converter.vue')['default']
8283
FormatTransformer: typeof import('./src/components/FormatTransformer.vue')['default']
8384
GitMemo: typeof import('./src/tools/git-memo/git-memo.vue')['default']
8485
'GitMemo.content': typeof import('./src/tools/git-memo/git-memo.content.md')['default']

locales/de.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,10 @@ tools:
399399
description: >-
400400
Konvertiere Zahlen zwischen verschiedenen Basen (Dezimal, Hexadezimal,
401401
Binär, Oktal, Base64, ...).
402+
floating-point-converter:
403+
title: Konverter für Fließkommazahlen
404+
description: >-
405+
Konvertiere eine Dezimalzahl in eine IEEE-754 binäre Fließkommazahl oder umgekehrt.
402406
yaml-to-json-converter:
403407
title: YAML zu JSON
404408
description: Konvertiere YAML einfach in JSON mit diesem Live-Online-Konverter.

locales/en.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,10 @@ tools:
344344
title: Integer base converter
345345
description: Convert a number between different bases (decimal, hexadecimal, binary, octal, base64, ...)
346346

347+
floating-point-converter:
348+
title: Floating point number converter
349+
description: Convert a decimal number to a IEEE 754 binary floating point number or vice versa.
350+
347351
yaml-to-json-converter:
348352
title: YAML to JSON converter
349353
description: Simply convert YAML to JSON with this online live converter.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { convertBinaryToDecimal, convertDecimalToBinary } from './floating-point-number-converter.model';
3+
4+
describe('floating-point-number-converter', () => {
5+
describe('convertDecimalToBinary', () => {
6+
it('Should convert a decimal number to a floating point binary number (IEEE-754)', () => {
7+
// 32-Bit
8+
expect(convertDecimalToBinary({ value: '0', bitCount: 32 })).toEqual('00000000000000000000000000000000');
9+
expect(convertDecimalToBinary({ value: '-0', bitCount: 32 })).toEqual('10000000000000000000000000000000');
10+
expect(convertDecimalToBinary({ value: 'NaN', bitCount: 32 })).toEqual('01111111110000000000000000000000');
11+
expect(convertDecimalToBinary({ value: 'Infinity', bitCount: 32 })).toEqual('01111111100000000000000000000000');
12+
expect(convertDecimalToBinary({ value: '-Infinity', bitCount: 32 })).toEqual('11111111100000000000000000000000');
13+
expect(convertDecimalToBinary({ value: '2.5', bitCount: 32 })).toEqual('01000000001000000000000000000000');
14+
expect(convertDecimalToBinary({ value: '-128.25', bitCount: 32 })).toEqual('11000011000000000100000000000000');
15+
expect(convertDecimalToBinary({ value: '0.1', bitCount: 32 })).toEqual('00111101110011001100110011001101');
16+
expect(convertDecimalToBinary({ value: '3.4028235e38', bitCount: 32 })).toEqual('01111111011111111111111111111111');
17+
expect(convertDecimalToBinary({ value: '1.1754942e-38', bitCount: 32 })).toEqual('00000000011111111111111111111111');
18+
19+
// 64-Bit
20+
expect(convertDecimalToBinary({ value: '0', bitCount: 64 })).toEqual('0000000000000000000000000000000000000000000000000000000000000000');
21+
expect(convertDecimalToBinary({ value: '-0', bitCount: 64 })).toEqual('1000000000000000000000000000000000000000000000000000000000000000');
22+
expect(convertDecimalToBinary({ value: 'NaN', bitCount: 64 })).toEqual('0111111111111000000000000000000000000000000000000000000000000000');
23+
expect(convertDecimalToBinary({ value: 'Infinity', bitCount: 64 })).toEqual('0111111111110000000000000000000000000000000000000000000000000000');
24+
expect(convertDecimalToBinary({ value: '-Infinity', bitCount: 64 })).toEqual('1111111111110000000000000000000000000000000000000000000000000000');
25+
expect(convertDecimalToBinary({ value: '2.5', bitCount: 64 })).toEqual('0100000000000100000000000000000000000000000000000000000000000000');
26+
expect(convertDecimalToBinary({ value: '-128.25', bitCount: 64 })).toEqual('1100000001100000000010000000000000000000000000000000000000000000');
27+
expect(convertDecimalToBinary({ value: '0.1', bitCount: 64 })).toEqual('0011111110111001100110011001100110011001100110011001100110011010');
28+
expect(convertDecimalToBinary({ value: '1.7976931348623157e308', bitCount: 64 })).toEqual('0111111111101111111111111111111111111111111111111111111111111111');
29+
expect(convertDecimalToBinary({ value: '2.225073858507201e-308', bitCount: 64 })).toEqual('0000000000001111111111111111111111111111111111111111111111111111');
30+
});
31+
});
32+
describe('convertBinaryToDecimal', () => {
33+
it('Should convert a floating point binary number (IEEE-754) to a decimal number', () => {
34+
// 32-Bit
35+
expect(convertBinaryToDecimal({ value: '00000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('0.00000000000000000000000000000000');
36+
expect(convertBinaryToDecimal({ value: '10000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('-0.00000000000000000000000000000000');
37+
expect(convertBinaryToDecimal({ value: '01111111110000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('NaN');
38+
expect(convertBinaryToDecimal({ value: '11111111110000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('NaN');
39+
expect(convertBinaryToDecimal({ value: '01111111110000000000000000000001', decimalPrecision: '32', removeZeroPadding: false })).toEqual('NaN');
40+
expect(convertBinaryToDecimal({ value: '01111111100000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('Infinity');
41+
expect(convertBinaryToDecimal({ value: '11111111100000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('-Infinity');
42+
expect(convertBinaryToDecimal({ value: '01000000001000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('2.50000000000000000000000000000000');
43+
expect(convertBinaryToDecimal({ value: '01000000001000000000000000000000', decimalPrecision: '32', removeZeroPadding: true })).toEqual('2.5');
44+
expect(convertBinaryToDecimal({ value: '11000011000000000100000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('-128.25000000000000000000000000000000');
45+
expect(convertBinaryToDecimal({ value: '00111101110011001100110011001101', decimalPrecision: '32', removeZeroPadding: false })).toEqual('0.10000000149011611938476562500000');
46+
expect(convertBinaryToDecimal({ value: '00111101110011001100110011001101', decimalPrecision: '10', removeZeroPadding: false })).toEqual('0.1000000015');
47+
expect(convertBinaryToDecimal({ value: '00111101110011001100110011001101', decimalPrecision: '1', removeZeroPadding: false })).toEqual('0.1');
48+
expect(convertBinaryToDecimal({ value: '01111111011111111111111111111111', decimalPrecision: '32', removeZeroPadding: false })).matches(/^3\.402823\d*e\+?38$/);
49+
expect(convertBinaryToDecimal({ value: '00000000011111111111111111111111', decimalPrecision: '32', removeZeroPadding: false })).toEqual('0.00000000000000000000000000000000');
50+
expect(convertBinaryToDecimal({ value: '00000000011111111111111111111111', decimalPrecision: '64', removeZeroPadding: false })).toEqual('0.0000000000000000000000000000000000000117549421069244107548702944');
51+
expect(convertBinaryToDecimal({ value: '00000000011111111111111111111111', decimalPrecision: '', removeZeroPadding: false })).matches(/^1\.1754942\d*e-38$/);
52+
53+
// 64-Bit
54+
expect(convertBinaryToDecimal({ value: '0000000000000000000000000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('0.00000000000000000000000000000000');
55+
expect(convertBinaryToDecimal({ value: '1000000000000000000000000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('-0.00000000000000000000000000000000');
56+
expect(convertBinaryToDecimal({ value: '0111111111111000000000000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('NaN');
57+
expect(convertBinaryToDecimal({ value: '1111111111111000000000000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('NaN');
58+
expect(convertBinaryToDecimal({ value: '0111111111111000000000000000000000000000000000000000000000000001', decimalPrecision: '32', removeZeroPadding: false })).toEqual('NaN');
59+
expect(convertBinaryToDecimal({ value: '0111111111110000000000000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('Infinity');
60+
expect(convertBinaryToDecimal({ value: '1111111111110000000000000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('-Infinity');
61+
expect(convertBinaryToDecimal({ value: '0100000000000100000000000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('2.50000000000000000000000000000000');
62+
expect(convertBinaryToDecimal({ value: '0100000000000100000000000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: true })).toEqual('2.5');
63+
expect(convertBinaryToDecimal({ value: '1100000001100000000010000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('-128.25000000000000000000000000000000');
64+
expect(convertBinaryToDecimal({ value: '0011111110111001100110011001100110011001100110011001100110011010', decimalPrecision: '32', removeZeroPadding: false })).toEqual('0.10000000000000000555111512312578');
65+
expect(convertBinaryToDecimal({ value: '0011111110111001100110011001100110011001100110011001100110011010', decimalPrecision: '10', removeZeroPadding: false })).toEqual('0.1000000000');
66+
expect(convertBinaryToDecimal({ value: '0011111110111001100110011001100110011001100110011001100110011010', decimalPrecision: '1', removeZeroPadding: false })).toEqual('0.1');
67+
expect(convertBinaryToDecimal({ value: '0111111111101111111111111111111111111111111111111111111111111111', decimalPrecision: '32', removeZeroPadding: false })).matches(/^1\.79769313\d*e\+?308$/);
68+
expect(convertBinaryToDecimal({ value: '0000000000001111111111111111111111111111111111111111111111111111', decimalPrecision: '32', removeZeroPadding: false })).toEqual('0.00000000000000000000000000000000');
69+
expect(convertBinaryToDecimal({ value: '0000000000001111111111111111111111111111111111111111111111111111', decimalPrecision: '100', removeZeroPadding: false })).toEqual('0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000');
70+
expect(convertBinaryToDecimal({ value: '0000000000001111111111111111111111111111111111111111111111111111', decimalPrecision: '', removeZeroPadding: false })).matches(/^2\.2250738585\d*e-308$/);
71+
});
72+
});
73+
});
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
export function convertDecimalToBinary({ value, bitCount }: { value: string; bitCount: number }) {
2+
let number = Number.parseFloat(value.replace(/\s/g, ''));
3+
if (value.match(/^-?inf(inity)?$/i)) {
4+
// const sign = value.startsWith('-') ? 1 : 0;
5+
// return `${sign}${'1'.repeat(exponentBits)}${'0'.repeat(mantissaBits)}`;
6+
number = (value.startsWith('-') ? -2 : 2) / 0;
7+
}
8+
9+
switch (bitCount) {
10+
case 32: { // Single precision
11+
const uint = new Uint32Array(Float32Array.of(number).buffer);
12+
return uint[0].toString(2).padStart(32, '0');
13+
}
14+
case 64: { // Double precision
15+
const uint = new Uint32Array(Float64Array.of(number).buffer);
16+
return [...uint].slice(0, 2).reverse().map(p => p.toString(2).padStart(32, '0')).join('');
17+
}
18+
default:
19+
throw new Error('Unsupported bit count. Only 32 and 64 are allowed.');
20+
}
21+
}
22+
23+
export function convertBinaryToDecimal({ value, decimalPrecision, removeZeroPadding }: { value: string; decimalPrecision: string; removeZeroPadding: boolean }) {
24+
if (value.match(/[^01]/)) {
25+
throw new Error('Not a binary number.');
26+
}
27+
if (decimalPrecision.match(/[^\d]/)) {
28+
throw new Error('Decimal Precision must be a positive whole number.');
29+
}
30+
31+
let result: number;
32+
switch (value.length) {
33+
case 32: {
34+
const binary = [Number.parseInt(value, 2)];
35+
result = (new Float32Array(Uint32Array.from(binary).buffer))[0];
36+
break;
37+
}
38+
case 64: {
39+
const binary = [Number.parseInt(value.substring(32), 2), Number.parseInt(value.substring(0, 32), 2)];
40+
result = (new Float64Array(Uint32Array.from(binary).buffer))[0];
41+
break;
42+
}
43+
default:
44+
throw new Error('Invalid length. Supply a binary string with length 32 or 64.');
45+
}
46+
47+
const zeroNegative = result === 0 && 2 / result === Number.NEGATIVE_INFINITY;
48+
let resultString = decimalPrecision.length === 0 ? result.toString() : result.toFixed(Number.parseInt(decimalPrecision));
49+
if (removeZeroPadding) {
50+
resultString = resultString.replace(/\.(\d+?)(0+)$/, '.$1');
51+
}
52+
return (zeroNegative ? '-' : '') + resultString;
53+
}
54+
55+
export function calcErrorDueToConversion({ decimalInput, actualValue }: { decimalInput: string; actualValue: string }) {
56+
return (Number.parseFloat(decimalInput) - Number.parseFloat(actualValue)).toFixed(32).replace(/\.(\d+?)(0+)$/, '.$1');
57+
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
<script setup lang="ts">
2+
import InputCopyable from '../../components/InputCopyable.vue';
3+
import { convertBase } from '../integer-base-converter/integer-base-converter.model';
4+
import { calcErrorDueToConversion, convertBinaryToDecimal, convertDecimalToBinary } from './floating-point-number-converter.model';
5+
import { getErrorMessageIfThrows } from '@/utils/error';
6+
7+
const bitCount = ref(32);
8+
const decimalInput = ref('42.42');
9+
const binaryOutput = ref();
10+
const actualValue = ref();
11+
12+
const binaryInput = ref('01000010001010011010111000010100');
13+
const decimalPrecision = ref('32');
14+
const showTrailingZeros = ref(false);
15+
16+
function errorlessBinaryToDecimalConversion(...args: Parameters<typeof convertBinaryToDecimal>) {
17+
try {
18+
return convertBinaryToDecimal(...args);
19+
}
20+
catch (err) {
21+
return '';
22+
}
23+
}
24+
25+
const binaryToDecimalError = computed(() =>
26+
getErrorMessageIfThrows(() =>
27+
convertBinaryToDecimal({ value: binaryInput.value, decimalPrecision: decimalPrecision.value, removeZeroPadding: false }),
28+
),
29+
);
30+
</script>
31+
32+
<template>
33+
<c-card title="Decimal to Binary" style="min-width: 650px">
34+
<c-input-text
35+
v-model:value="decimalInput"
36+
label="Decimal Number"
37+
placeholder="Put your decimal number here (ex: 42.42)"
38+
label-position="left"
39+
label-width="210px"
40+
label-align="right"
41+
mb-2
42+
/>
43+
44+
<c-select
45+
v-model:value="bitCount"
46+
mb-4
47+
label="Bit Count"
48+
label-position="left"
49+
label-width="210px"
50+
label-align="right"
51+
:options="[
52+
{
53+
label: '32-Bit (Single precision)',
54+
value: 32,
55+
},
56+
{
57+
label: '64-Bit (Double precision)',
58+
value: 64,
59+
},
60+
]"
61+
/>
62+
63+
<n-divider />
64+
65+
<InputCopyable
66+
label="Binary Number"
67+
placeholder="Binary Number"
68+
:value="binaryOutput = convertDecimalToBinary({ value: decimalInput, bitCount })"
69+
readonly
70+
label-position="left"
71+
label-width="210px"
72+
label-align="right"
73+
mb-2
74+
/>
75+
76+
<InputCopyable
77+
label="Hexadecimal Representation"
78+
placeholder="Hexadecimal Representation"
79+
:value="convertBase({ value: binaryOutput, fromBase: 2, toBase: 16 })"
80+
readonly
81+
label-position="left"
82+
label-width="210px"
83+
label-align="right"
84+
mb-2
85+
/>
86+
87+
<InputCopyable
88+
label="Actually stored value"
89+
placeholder="Actually stored value"
90+
:value="actualValue = errorlessBinaryToDecimalConversion({ value: binaryOutput, decimalPrecision: '32', removeZeroPadding: true })"
91+
readonly
92+
label-position="left"
93+
label-width="210px"
94+
label-align="right"
95+
mb-2
96+
/>
97+
98+
<InputCopyable
99+
label="Error due to conversion"
100+
placeholder="Error due to conversion"
101+
:value="calcErrorDueToConversion({ decimalInput, actualValue })"
102+
readonly
103+
label-position="left"
104+
label-width="210px"
105+
label-align="right"
106+
mb-2
107+
/>
108+
</c-card>
109+
110+
<c-card title="Binary to Decimal" style="min-width: 650px">
111+
<c-input-text
112+
v-model:value="binaryInput"
113+
label="Binary Number"
114+
placeholder="Put your binary number here (ex: 01000010001010011010111000010100)"
115+
label-position="left"
116+
label-width="140px"
117+
label-align="right"
118+
mb-2
119+
/>
120+
121+
<c-input-text
122+
v-model:value="decimalPrecision"
123+
label="Decimal Precision"
124+
placeholder="Choose the decimal precision (digits after the decimal point)."
125+
label-position="left"
126+
label-width="140px"
127+
label-align="right"
128+
mb-2
129+
/>
130+
131+
<n-form-item
132+
label="Show Trailing Zeros"
133+
label-placement="left"
134+
label-width="140px"
135+
label-align="right"
136+
>
137+
<n-switch
138+
v-model:value="showTrailingZeros"
139+
/>
140+
</n-form-item>
141+
142+
<n-alert v-if="binaryToDecimalError" style="margin-top: 25px" type="error">
143+
{{ binaryToDecimalError }}
144+
</n-alert>
145+
146+
<n-divider />
147+
148+
<InputCopyable
149+
label="Decimal Number"
150+
placeholder="Decimal Number"
151+
:value="errorlessBinaryToDecimalConversion({ value: binaryInput, decimalPrecision, removeZeroPadding: !showTrailingZeros })"
152+
readonly
153+
label-position="left"
154+
label-width="140px"
155+
label-align="right"
156+
mb-2
157+
/>
158+
</c-card>
159+
</template>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { ArrowsLeftRight } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
import { translate } from '@/plugins/i18n.plugin';
4+
5+
export const tool = defineTool({
6+
name: translate('tools.floating-point-converter.title'),
7+
path: '/floating-point-converter',
8+
description: translate('tools.floating-point-converter.description'),
9+
keywords: ['converter', 'floating', 'point', 'number', 'converter', 'binary', 'decimal'],
10+
component: () => import('./floating-point-number-converter.vue'),
11+
icon: ArrowsLeftRight,
12+
createdAt: new Date('2024-10-12'),
13+
});

src/tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { tool as base64FileConverter } from './base64-file-converter';
22
import { tool as base64StringConverter } from './base64-string-converter';
33
import { tool as basicAuthGenerator } from './basic-auth-generator';
4+
import { tool as floatingPointNumberConverter } from './floating-point-number-converter';
45
import { tool as emailNormalizer } from './email-normalizer';
56

67
import { tool as asciiTextDrawer } from './ascii-text-drawer';
@@ -98,6 +99,7 @@ export const toolsByCategory: ToolCategory[] = [
9899
components: [
99100
dateTimeConverter,
100101
baseConverter,
102+
floatingPointNumberConverter,
101103
romanNumeralConverter,
102104
base64StringConverter,
103105
base64FileConverter,

0 commit comments

Comments
 (0)