1

I have used this code to split into parts

How to find the numbers in the thousands, hundreds, tens, and ones place in DELPHI for an input number? For example: 155 has 5 ones, 5 tens, etc

however I now need to handle floats.

eg. 101.01

type TSplitNumber = record
Hundreds : integer
Tens : integer
Ones : integer
DecimalPl1 : integer //this will contain 0
DecimalPl2 : integer //this will contain 1
DecimalPl3 : integer
end;

Heres is implementation so far but it only handles non-floats.

type TDivisions = record
  Hundreds : integer;
  Tens : integer;
  Ones : integer;
end;

function SplitNumberIntoDivisions(number : integer) : TDivisions;
var
  p : integer;
  Ones : integer;
  Tens : integer;
  Hundreds : integer;
  MyDivision : TDivisions;
begin
  p := 1;
  while number <> 0 do
  begin
    if p = 1 then
    begin
      MyDivision.Ones := (number mod 10);
      number := number div 10;
      Inc(p);
    end;
    if p = 2 then
    begin
      MyDivision.Tens := (number mod 10);
      number := number div 10;
      Inc(p);
    end;
    if p = 3 then
    begin
      MyDivision.Hundreds := (number mod 10);
      number := number div 10;
      Inc(p);
    end;

    Result := MyDivision;
    end;
end;

Anyone got any idea on how to do this?

Ben

fpiette
  • 11,983
  • 1
  • 24
  • 46
Ben
  • 565
  • 1
  • 3
  • 11
  • The "idea" would be to turn that number into text and handle it per character, or? – AmigoJack Jul 06 '21 at 19:46
  • Also, when you look at your code, do you get the feeling that you are [repeating yourself](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)? – Andreas Rejbrand Jul 06 '21 at 19:58
  • Since this encapsulates the basic problem of representing a binary value as a decimal string, the really easy way to do this is to use `FloatToStrF` to format the number of places you want to show, then just go character by character and read off the digits with `StrToInt`, minding the `.`, of course. – J... Jul 06 '21 at 20:13
  • @Andreas. Yes I was repeating myself. From "if" to "end" could be put into a function for code reuse. Thanks – Ben Jul 06 '21 at 21:00

1 Answers1

1

First of all, recognise what your float is. Depending on the architecture you will have a certain number of significant digits. Upto 15 is typical but certain architectures may (at some point) give you more, and BCD as implemented in the RTL will give you up to 64.

You then have a 'power' indicating where the decimal point is. Typically you refer to the parts of the float as the mantissa and exponent.

So your answer is going to comprise a set of dgits, each digit being a power of 10, where the powers of 10 are all consecutive and may be all >0 or all <0 or they could straddle 0.

So you will need a structure to hold your powers of 10 which could be something like:

type TDecimalFactor = class(TObject)
  Digit:     Integer;
  Power:     Integer;
end;

You can find out what your largest power of 10 is by taking the base 10 log of the number. (So log(100) is 2 and log(1000) is 3 and log(0.1) is -1).

I suggest it would probably be fairly straightforward to 'normalise' your number by dividing it by the highest power - so you have a number which is between 1 and 9.999999999999999 and you know the power it represents. Then work through the number for a many digits as you want (bearing in mind the resolution of the platform) multiplying the float by 10 each time and decrementing your power by 1.

Sample program for you to play with:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Math, System.Generics.Collections;

  type
  TDecimalFactor = class(TObject)
  protected
    _nDigit:     Integer;
    _nPower:     Integer;

    function  _GetValue(): Double;
  public
    constructor Create(nDigit, nPower: Integer);

    procedure ShowFactor();

    property  Digit: Integer read _nDigit;
    property  Power: Integer read _nPower;
    property  Value: Double read _GetValue;
  end;

  TDecimalFactors = class(TObjectList<TDecimalFactor>)
  protected
    function  _GetValue(): Double;
  public
    property  Value: Double read _GetValue;
  end;

  constructor TDecimalFactor.Create(nDigit, nPower: Integer);
  begin
    Self._nDigit:=nDigit;
    Self._nPower:=nPower;
    inherited Create();
  end;

  function TDecimalFactor._GetValue(): Double;
  begin
    Result:=Self._nDigit*System.Math.Power(10, Self._nPower);
  end;

  procedure TDecimalFactor.ShowFactor();
  begin
    writeln('Factor:  ', IntToStr(Self._nDigit), ' x ', FloatToStr(System.Math.Power( 10, Self._nPower)));
  end;

  function TDecimalFactors._GetValue(): Double;
  var
    pFactor:      TDecimalFactor;
  begin
    Result:=0;
    for pFactor in Self do
        Result:=Result+pFactor.Value;
  end;

var
  fValue:   Double;
  fLog:     Double;
  nPower:   Integer;
  fPower:   Double;
  nDigits:  Integer;
  nLoop:    Integer;
  pDigits:  TDecimalFactors;
  pFactor:  TDecimalFactor;
begin
  try
    pDigits:=TDecimalFactors.Create(true);
    fValue:=6.5788902E-5;    // try different values here to test operation
    writeln('Input Value: '+FloatToStr(fValue));
    nDigits:=15;
    fLog:=log10(fValue);
    nPower:=floor(fLog);
    fPower:=Power(10,nPower);
    fValue:=fValue/fPower;
    nLoop:=0;
    while(nLoop<nDigits) do
    begin
      pFactor:=TDecimalFactor.Create(floor(fValue), nPower);
      pDigits.Add(pFactor);
      pFactor.ShowFactor();
      fValue:=(fValue-pFactor.Digit)*10;
      inc(nLoop);
      dec(nPower);
      // stop the loop when we have got far enough, recognising limited precision
      if(SameValue(fValue, 0, 0.00000001)) then
        break;
    end;
    writeln('Factorised Value: '+FloatToStr(pDigits.Value));
    FreeAndNil(pDigits);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
Rob Lambden
  • 2,175
  • 6
  • 15