Ben
Ben

Reputation: 575

How do I split any number into its parts?

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

Upvotes: 2

Views: 664

Answers (1)

Rob Lambden
Rob Lambden

Reputation: 2293

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.

Upvotes: 1

Related Questions