-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add tool floating-point-number-converter.
- Loading branch information
Showing
8 changed files
with
309 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
73 changes: 73 additions & 0 deletions
73
src/tools/floating-point-number-converter/floating-point-number-converter.model.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { describe, expect, it } from 'vitest'; | ||
import { convertBinaryToDecimal, convertDecimalToBinary } from './floating-point-number-converter.model'; | ||
|
||
describe('floating-point-number-converter', () => { | ||
describe('convertDecimalToBinary', () => { | ||
it('Should convert a decimal number to a floating point binary number (IEEE-754)', () => { | ||
// 32-Bit | ||
expect(convertDecimalToBinary({ value: '0', bitCount: 32 })).toEqual('00000000000000000000000000000000'); | ||
expect(convertDecimalToBinary({ value: '-0', bitCount: 32 })).toEqual('10000000000000000000000000000000'); | ||
expect(convertDecimalToBinary({ value: 'NaN', bitCount: 32 })).toEqual('01111111110000000000000000000000'); | ||
expect(convertDecimalToBinary({ value: 'Infinity', bitCount: 32 })).toEqual('01111111100000000000000000000000'); | ||
expect(convertDecimalToBinary({ value: '-Infinity', bitCount: 32 })).toEqual('11111111100000000000000000000000'); | ||
expect(convertDecimalToBinary({ value: '2.5', bitCount: 32 })).toEqual('01000000001000000000000000000000'); | ||
expect(convertDecimalToBinary({ value: '-128.25', bitCount: 32 })).toEqual('11000011000000000100000000000000'); | ||
expect(convertDecimalToBinary({ value: '0.1', bitCount: 32 })).toEqual('00111101110011001100110011001101'); | ||
expect(convertDecimalToBinary({ value: '3.4028235e38', bitCount: 32 })).toEqual('01111111011111111111111111111111'); | ||
expect(convertDecimalToBinary({ value: '1.1754942e-38', bitCount: 32 })).toEqual('00000000011111111111111111111111'); | ||
|
||
// 64-Bit | ||
expect(convertDecimalToBinary({ value: '0', bitCount: 64 })).toEqual('0000000000000000000000000000000000000000000000000000000000000000'); | ||
expect(convertDecimalToBinary({ value: '-0', bitCount: 64 })).toEqual('1000000000000000000000000000000000000000000000000000000000000000'); | ||
expect(convertDecimalToBinary({ value: 'NaN', bitCount: 64 })).toEqual('0111111111111000000000000000000000000000000000000000000000000000'); | ||
expect(convertDecimalToBinary({ value: 'Infinity', bitCount: 64 })).toEqual('0111111111110000000000000000000000000000000000000000000000000000'); | ||
expect(convertDecimalToBinary({ value: '-Infinity', bitCount: 64 })).toEqual('1111111111110000000000000000000000000000000000000000000000000000'); | ||
expect(convertDecimalToBinary({ value: '2.5', bitCount: 64 })).toEqual('0100000000000100000000000000000000000000000000000000000000000000'); | ||
expect(convertDecimalToBinary({ value: '-128.25', bitCount: 64 })).toEqual('1100000001100000000010000000000000000000000000000000000000000000'); | ||
expect(convertDecimalToBinary({ value: '0.1', bitCount: 64 })).toEqual('0011111110111001100110011001100110011001100110011001100110011010'); | ||
expect(convertDecimalToBinary({ value: '1.7976931348623157e308', bitCount: 64 })).toEqual('0111111111101111111111111111111111111111111111111111111111111111'); | ||
expect(convertDecimalToBinary({ value: '2.225073858507201e-308', bitCount: 64 })).toEqual('0000000000001111111111111111111111111111111111111111111111111111'); | ||
}); | ||
}); | ||
describe('convertBinaryToDecimal', () => { | ||
it('Should convert a floating point binary number (IEEE-754) to a decimal number', () => { | ||
// 32-Bit | ||
expect(convertBinaryToDecimal({ value: '00000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('0.00000000000000000000000000000000'); | ||
expect(convertBinaryToDecimal({ value: '10000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('-0.00000000000000000000000000000000'); | ||
expect(convertBinaryToDecimal({ value: '01111111110000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('NaN'); | ||
expect(convertBinaryToDecimal({ value: '11111111110000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('NaN'); | ||
expect(convertBinaryToDecimal({ value: '01111111110000000000000000000001', decimalPrecision: '32', removeZeroPadding: false })).toEqual('NaN'); | ||
expect(convertBinaryToDecimal({ value: '01111111100000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('Infinity'); | ||
expect(convertBinaryToDecimal({ value: '11111111100000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('-Infinity'); | ||
expect(convertBinaryToDecimal({ value: '01000000001000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('2.50000000000000000000000000000000'); | ||
expect(convertBinaryToDecimal({ value: '01000000001000000000000000000000', decimalPrecision: '32', removeZeroPadding: true })).toEqual('2.5'); | ||
expect(convertBinaryToDecimal({ value: '11000011000000000100000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('-128.25000000000000000000000000000000'); | ||
expect(convertBinaryToDecimal({ value: '00111101110011001100110011001101', decimalPrecision: '32', removeZeroPadding: false })).toEqual('0.10000000149011611938476562500000'); | ||
expect(convertBinaryToDecimal({ value: '00111101110011001100110011001101', decimalPrecision: '10', removeZeroPadding: false })).toEqual('0.1000000015'); | ||
expect(convertBinaryToDecimal({ value: '00111101110011001100110011001101', decimalPrecision: '1', removeZeroPadding: false })).toEqual('0.1'); | ||
expect(convertBinaryToDecimal({ value: '01111111011111111111111111111111', decimalPrecision: '32', removeZeroPadding: false })).matches(/^3\.402823\d*e\+?38$/); | ||
expect(convertBinaryToDecimal({ value: '00000000011111111111111111111111', decimalPrecision: '32', removeZeroPadding: false })).toEqual('0.00000000000000000000000000000000'); | ||
expect(convertBinaryToDecimal({ value: '00000000011111111111111111111111', decimalPrecision: '64', removeZeroPadding: false })).toEqual('0.0000000000000000000000000000000000000117549421069244107548702944'); | ||
expect(convertBinaryToDecimal({ value: '00000000011111111111111111111111', decimalPrecision: '', removeZeroPadding: false })).matches(/^1\.1754942\d*e-38$/); | ||
|
||
// 64-Bit | ||
expect(convertBinaryToDecimal({ value: '0000000000000000000000000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('0.00000000000000000000000000000000'); | ||
expect(convertBinaryToDecimal({ value: '1000000000000000000000000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('-0.00000000000000000000000000000000'); | ||
expect(convertBinaryToDecimal({ value: '0111111111111000000000000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('NaN'); | ||
expect(convertBinaryToDecimal({ value: '1111111111111000000000000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('NaN'); | ||
expect(convertBinaryToDecimal({ value: '0111111111111000000000000000000000000000000000000000000000000001', decimalPrecision: '32', removeZeroPadding: false })).toEqual('NaN'); | ||
expect(convertBinaryToDecimal({ value: '0111111111110000000000000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('Infinity'); | ||
expect(convertBinaryToDecimal({ value: '1111111111110000000000000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('-Infinity'); | ||
expect(convertBinaryToDecimal({ value: '0100000000000100000000000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('2.50000000000000000000000000000000'); | ||
expect(convertBinaryToDecimal({ value: '0100000000000100000000000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: true })).toEqual('2.5'); | ||
expect(convertBinaryToDecimal({ value: '1100000001100000000010000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('-128.25000000000000000000000000000000'); | ||
expect(convertBinaryToDecimal({ value: '0011111110111001100110011001100110011001100110011001100110011010', decimalPrecision: '32', removeZeroPadding: false })).toEqual('0.10000000000000000555111512312578'); | ||
expect(convertBinaryToDecimal({ value: '0011111110111001100110011001100110011001100110011001100110011010', decimalPrecision: '10', removeZeroPadding: false })).toEqual('0.1000000000'); | ||
expect(convertBinaryToDecimal({ value: '0011111110111001100110011001100110011001100110011001100110011010', decimalPrecision: '1', removeZeroPadding: false })).toEqual('0.1'); | ||
expect(convertBinaryToDecimal({ value: '0111111111101111111111111111111111111111111111111111111111111111', decimalPrecision: '32', removeZeroPadding: false })).matches(/^1\.79769313\d*e\+?308$/); | ||
expect(convertBinaryToDecimal({ value: '0000000000001111111111111111111111111111111111111111111111111111', decimalPrecision: '32', removeZeroPadding: false })).toEqual('0.00000000000000000000000000000000'); | ||
expect(convertBinaryToDecimal({ value: '0000000000001111111111111111111111111111111111111111111111111111', decimalPrecision: '100', removeZeroPadding: false })).toEqual('0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); | ||
expect(convertBinaryToDecimal({ value: '0000000000001111111111111111111111111111111111111111111111111111', decimalPrecision: '', removeZeroPadding: false })).matches(/^2\.2250738585\d*e-308$/); | ||
}); | ||
}); | ||
}); |
53 changes: 53 additions & 0 deletions
53
src/tools/floating-point-number-converter/floating-point-number-converter.model.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
export function convertDecimalToBinary({ value, bitCount }: { value: string; bitCount: number }) { | ||
let number = Number.parseFloat(value.replace(/\s/g, '')); | ||
if (value.match(/^-?inf(inity)?$/i)) { | ||
// const sign = value.startsWith('-') ? 1 : 0; | ||
// return `${sign}${'1'.repeat(exponentBits)}${'0'.repeat(mantissaBits)}`; | ||
number = (value.startsWith('-') ? -2 : 2) / 0; | ||
} | ||
|
||
switch (bitCount) { | ||
case 32: { // Single precision | ||
const uint = new Uint32Array(Float32Array.of(number).buffer); | ||
return uint[0].toString(2).padStart(32, '0'); | ||
} | ||
case 64: { // Double precision | ||
const uint = new Uint32Array(Float64Array.of(number).buffer); | ||
return [...uint].slice(0, 2).reverse().map(p => p.toString(2).padStart(32, '0')).join(''); | ||
} | ||
default: | ||
throw new Error('Unsupported bit count. Only 32 and 64 are allowed.'); | ||
} | ||
} | ||
|
||
export function convertBinaryToDecimal({ value, decimalPrecision, removeZeroPadding }: { value: string; decimalPrecision: string; removeZeroPadding: boolean }) { | ||
if (value.match(/[^01]/)) { | ||
throw new Error('Not a binary number.'); | ||
} | ||
if (decimalPrecision.match(/[^\d]/)) { | ||
throw new Error('Decimal Precision must be a positive whole number.'); | ||
} | ||
|
||
let result: number; | ||
switch (value.length) { | ||
case 32: { | ||
const binary = [Number.parseInt(value, 2)]; | ||
result = (new Float32Array(Uint32Array.from(binary).buffer))[0]; | ||
break; | ||
} | ||
case 64: { | ||
const binary = [Number.parseInt(value.substring(32), 2), Number.parseInt(value.substring(0, 32), 2)]; | ||
result = (new Float64Array(Uint32Array.from(binary).buffer))[0]; | ||
break; | ||
} | ||
default: | ||
throw new Error('Invalid length. Supply a binary string with length 32 or 64.'); | ||
} | ||
|
||
const zeroNegative = result === 0 && 2 / result === Number.NEGATIVE_INFINITY; | ||
let resultString = decimalPrecision.length === 0 ? result.toString() : result.toFixed(Number.parseInt(decimalPrecision)); | ||
if (removeZeroPadding) { | ||
resultString = resultString.replace(/\.(\d+?)(0+)$/, '.$1'); | ||
} | ||
return (zeroNegative ? '-' : '') + resultString; | ||
} |
159 changes: 159 additions & 0 deletions
159
src/tools/floating-point-number-converter/floating-point-number-converter.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
<script setup lang="ts"> | ||
import InputCopyable from '../../components/InputCopyable.vue'; | ||
import { convertBase } from '../integer-base-converter/integer-base-converter.model'; | ||
import { convertBinaryToDecimal, convertDecimalToBinary } from './floating-point-number-converter.model'; | ||
import { getErrorMessageIfThrows } from '@/utils/error'; | ||
const bitCount = ref(32); | ||
const decimalInput = ref('42.42'); | ||
const binaryOutput = ref(); | ||
const actualValue = ref(); | ||
const binaryInput = ref('01000010001010011010111000010100'); | ||
const decimalPrecision = ref('32'); | ||
const showTrailingZeros = ref(false); | ||
function errorlessBinaryToDecimalConversion(...args: Parameters<typeof convertBinaryToDecimal>) { | ||
try { | ||
return convertBinaryToDecimal(...args); | ||
} | ||
catch (err) { | ||
return ''; | ||
} | ||
} | ||
const binaryToDecimalError = computed(() => | ||
getErrorMessageIfThrows(() => | ||
convertBinaryToDecimal({ value: binaryInput.value, decimalPrecision: decimalPrecision.value, removeZeroPadding: false }), | ||
), | ||
); | ||
</script> | ||
|
||
<template> | ||
<c-card title="Decimal to Binary" style="min-width: 650px"> | ||
<c-input-text | ||
v-model:value="decimalInput" | ||
label="Decimal Number" | ||
placeholder="Put your decimal number here (ex: 42.42)" | ||
label-position="left" | ||
label-width="210px" | ||
label-align="right" | ||
mb-2 | ||
/> | ||
|
||
<c-select | ||
v-model:value="bitCount" | ||
mb-4 | ||
label="Bit Count" | ||
label-position="left" | ||
label-width="210px" | ||
label-align="right" | ||
:options="[ | ||
{ | ||
label: '32-Bit (Single precision)', | ||
value: 32, | ||
}, | ||
{ | ||
label: '64-Bit (Double precision)', | ||
value: 64, | ||
}, | ||
]" | ||
/> | ||
|
||
<n-divider /> | ||
|
||
<InputCopyable | ||
label="Binary Number" | ||
placeholder="Binary Number" | ||
:value="binaryOutput = convertDecimalToBinary({ value: decimalInput, bitCount })" | ||
readonly | ||
label-position="left" | ||
label-width="210px" | ||
label-align="right" | ||
mb-2 | ||
/> | ||
|
||
<InputCopyable | ||
label="Hexadecimal Representation" | ||
placeholder="Hexadecimal Representation" | ||
:value="convertBase({ value: binaryOutput, fromBase: 2, toBase: 16 })" | ||
readonly | ||
label-position="left" | ||
label-width="210px" | ||
label-align="right" | ||
mb-2 | ||
/> | ||
|
||
<InputCopyable | ||
label="Actually stored value" | ||
placeholder="Actually stored value" | ||
:value="actualValue = errorlessBinaryToDecimalConversion({ value: binaryOutput, decimalPrecision: '32', removeZeroPadding: true })" | ||
readonly | ||
label-position="left" | ||
label-width="210px" | ||
label-align="right" | ||
mb-2 | ||
/> | ||
|
||
<InputCopyable | ||
label="Error due to conversion" | ||
placeholder="Error due to conversion" | ||
:value="(decimalInput - actualValue).toFixed(32).replace(/\.(\d+?)(0+)$/, '.$1')" | ||
readonly | ||
label-position="left" | ||
label-width="210px" | ||
label-align="right" | ||
mb-2 | ||
/> | ||
</c-card> | ||
|
||
<c-card title="Binary to Decimal" style="min-width: 650px"> | ||
<c-input-text | ||
v-model:value="binaryInput" | ||
label="Binary Number" | ||
placeholder="Put your binary number here (ex: 01000010001010011010111000010100)" | ||
label-position="left" | ||
label-width="140px" | ||
label-align="right" | ||
mb-2 | ||
/> | ||
|
||
<c-input-text | ||
v-model:value="decimalPrecision" | ||
label="Decimal Precision" | ||
placeholder="Choose the decimal precision (digits after the decimal point)." | ||
label-position="left" | ||
label-width="140px" | ||
label-align="right" | ||
mb-2 | ||
/> | ||
|
||
<n-form-item | ||
label="Show Trailing Zeros" | ||
label-placement="left" | ||
label-width="140px" | ||
label-align="right" | ||
> | ||
<n-switch | ||
v-model:value="showTrailingZeros" | ||
/> | ||
</n-form-item> | ||
|
||
<n-alert v-if="binaryToDecimalError" style="margin-top: 25px" type="error"> | ||
{{ binaryToDecimalError }} | ||
</n-alert> | ||
|
||
<n-divider /> | ||
|
||
<InputCopyable | ||
label="Decimal Number" | ||
placeholder="Decimal Number" | ||
:value="errorlessBinaryToDecimalConversion({ value: binaryInput, decimalPrecision, removeZeroPadding: !showTrailingZeros })" | ||
readonly | ||
label-position="left" | ||
label-width="140px" | ||
label-align="right" | ||
mb-2 | ||
/> | ||
</c-card> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { ArrowsLeftRight } from '@vicons/tabler'; | ||
import { defineTool } from '../tool'; | ||
import { translate } from '@/plugins/i18n.plugin'; | ||
|
||
export const tool = defineTool({ | ||
name: translate('tools.floating-point-converter.title'), | ||
path: '/floating-point-converter', | ||
description: translate('tools.floating-point-converter.description'), | ||
keywords: ['converter', 'floating', 'point', 'number', 'converter', 'binary', 'decimal'], | ||
component: () => import('./floating-point-number-converter.vue'), | ||
icon: ArrowsLeftRight, | ||
createdAt: new Date('2024-10-12'), | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters