-
-
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
302 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
74 changes: 74 additions & 0 deletions
74
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,74 @@ | ||
import { expect, describe, it } from 'vitest'; | ||
import { convertDecimalToBinary } from './floating-point-number-converter.model'; | ||
Check warning on line 2 in src/tools/floating-point-number-converter/floating-point-number-converter.model.test.ts GitHub Actions / ci
|
||
import { convertBinaryToDecimal } from './floating-point-number-converter.model'; | ||
Check warning on line 3 in src/tools/floating-point-number-converter/floating-point-number-converter.model.test.ts GitHub Actions / ci
|
||
|
||
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.' + '0'.repeat(32)); | ||
expect(convertBinaryToDecimal({ value: '10000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('-0.' + '0'.repeat(32)); | ||
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.5' + '0'.repeat(31)); | ||
expect(convertBinaryToDecimal({ value: '01000000001000000000000000000000', decimalPrecision: '32', removeZeroPadding: true })).toEqual('2.5'); | ||
expect(convertBinaryToDecimal({ value: '11000011000000000100000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('-128.25' + '0'.repeat(30)); | ||
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.' + '0'.repeat(32)); | ||
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.' + '0'.repeat(32)); | ||
expect(convertBinaryToDecimal({ value: '1000000000000000000000000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('-0.' + '0'.repeat(32)); | ||
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.5' + '0'.repeat(31)); | ||
expect(convertBinaryToDecimal({ value: '0100000000000100000000000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: true })).toEqual('2.5'); | ||
expect(convertBinaryToDecimal({ value: '1100000001100000000010000000000000000000000000000000000000000000', decimalPrecision: '32', removeZeroPadding: false })).toEqual('-128.25' + '0'.repeat(30)); | ||
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.' + '0'.repeat(32)); | ||
expect(convertBinaryToDecimal({ value: '0000000000001111111111111111111111111111111111111111111111111111', decimalPrecision: '100', removeZeroPadding: false })).toEqual('0.' + '0'.repeat(100)); | ||
expect(convertBinaryToDecimal({ value: '0000000000001111111111111111111111111111111111111111111111111111', decimalPrecision: '', removeZeroPadding: false })).matches(/^2\.2250738585\d*e-308$/); | ||
}); | ||
}); | ||
}); |
46 changes: 46 additions & 0 deletions
46
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,46 @@ | ||
export function convertDecimalToBinary({ value, bitCount }: { value: string; bitCount: number }) { | ||
let 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 | ||
var uint = new Uint32Array(Float32Array.of(number).buffer); | ||
return uint[0].toString(2).padStart(32, '0'); | ||
case 64: // Double precision | ||
var 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: | ||
var binary = [parseInt(value, 2)]; | ||
result = (new Float32Array(Uint32Array.from(binary).buffer))[0]; | ||
break; | ||
case 64: | ||
var binary = [parseInt(value.substring(32), 2), 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."); | ||
} | ||
|
||
let zeroNegative = result == 0 && 2 / result == -Infinity; | ||
let resultString = decimalPrecision.length == 0 ? result.toString() : result.toFixed(parseInt(decimalPrecision)); | ||
if (removeZeroPadding) | ||
resultString = resultString.replace(/\.(\d+?)(0+)$/, '.$1'); | ||
return (zeroNegative ? '-' : '') + resultString; | ||
} |
158 changes: 158 additions & 0 deletions
158
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,158 @@ | ||
<script setup lang="ts"> | ||
import InputCopyable from '../../components/InputCopyable.vue'; | ||
import { convertDecimalToBinary } from './floating-point-number-converter.model'; | ||
Check warning on line 3 in src/tools/floating-point-number-converter/floating-point-number-converter.vue GitHub Actions / ci
|
||
import { convertBinaryToDecimal } from './floating-point-number-converter.model'; | ||
Check warning on line 4 in src/tools/floating-point-number-converter/floating-point-number-converter.vue GitHub Actions / ci
|
||
import { convertBase } from '../integer-base-converter/integer-base-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: 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: 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